homebridge-weather-noaa
Version:
Homebridge plugin providing temperature and humidity sensors using NOAA API.
138 lines (137 loc) • 6.9 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NOAAWeatherAccessory = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
class NOAAWeatherAccessory {
constructor(platform, accessory) {
this.platform = platform;
this.accessory = accessory;
this.lastTemperature = null;
this.lastHumidity = null;
try {
const persistPath = this.platform.api.user.persistPath();
this.cacheFile = path_1.default.join(persistPath, 'noaa-weather-last.json');
let lastWeather = { temperature: 20, humidity: 50 };
if (fs_1.default.existsSync(this.cacheFile)) {
try {
lastWeather = JSON.parse(fs_1.default.readFileSync(this.cacheFile, 'utf8'));
}
catch (e) {
NOAAWeatherAccessory.metrics.cacheResets++;
this.logWarn('Corrupted weather cache detected. Resetting cache.', e);
try {
fs_1.default.unlinkSync(this.cacheFile);
}
catch { }
}
}
this.lastTemperature = lastWeather.temperature;
this.lastHumidity = lastWeather.humidity;
accessory.context.weather = lastWeather;
this.temperatureService =
accessory.getService(this.platform.api.hap.Service.TemperatureSensor) ||
accessory.addService(this.platform.api.hap.Service.TemperatureSensor, 'NOAA Temperature', this.platform.api.hap.Service.TemperatureSensor.UUID);
this.humidityService =
accessory.getService(this.platform.api.hap.Service.HumiditySensor) ||
accessory.addService(this.platform.api.hap.Service.HumiditySensor, 'NOAA Humidity', this.platform.api.hap.Service.HumiditySensor.UUID);
this.safeUpdateCharacteristic(this.temperatureService, this.platform.api.hap.Characteristic.CurrentTemperature, lastWeather.temperature);
this.safeUpdateCharacteristic(this.humidityService, this.platform.api.hap.Characteristic.CurrentRelativeHumidity, lastWeather.humidity);
this.logInfo(`Initialized HomeKit with cached NOAA weather: ${lastWeather.temperature}°C, ${lastWeather.humidity}%`);
setInterval(() => this.logMetrics(), 60 * 60 * 1000);
process.on('exit', () => this.logMetrics());
}
catch (err) {
this.logError('Failed during NOAAWeatherAccessory constructor:', err);
}
}
updateValues() {
try {
const weather = this.accessory.context.weather;
if (weather.temperature === null || weather.humidity === null) {
this.logWarn('NOAA returned null values. Keeping last known HomeKit values.');
return;
}
this.logInfo(`Pushed NOAA weather to HomeKit: ${weather.temperature}°C, ${weather.humidity}%`);
this.safeUpdateCharacteristic(this.temperatureService, this.platform.api.hap.Characteristic.CurrentTemperature, weather.temperature);
this.safeUpdateCharacteristic(this.humidityService, this.platform.api.hap.Characteristic.CurrentRelativeHumidity, weather.humidity);
try {
fs_1.default.writeFileSync(this.cacheFile, JSON.stringify(weather));
}
catch (e) {
NOAAWeatherAccessory.metrics.cacheWriteErrors++;
this.logError('Failed to write last weather cache:', e);
}
this.lastTemperature = weather.temperature;
this.lastHumidity = weather.humidity;
}
catch (err) {
this.logError('Error updating NOAA weather values:', err);
}
}
safeUpdateCharacteristic(service, characteristic, value) {
try {
service.updateCharacteristic(characteristic, value);
}
catch (e) {
NOAAWeatherAccessory.metrics.characteristicErrors++;
this.logError(`Failed to update HomeKit characteristic ${characteristic.displayName || characteristic}:`, e);
try {
let newService = null;
if (service.UUID === this.platform.api.hap.Service.TemperatureSensor.UUID) {
newService = this.accessory.addService(this.platform.api.hap.Service.TemperatureSensor, 'NOAA Temperature', this.platform.api.hap.Service.TemperatureSensor.UUID);
}
else if (service.UUID === this.platform.api.hap.Service.HumiditySensor.UUID) {
newService = this.accessory.addService(this.platform.api.hap.Service.HumiditySensor, 'NOAA Humidity', this.platform.api.hap.Service.HumiditySensor.UUID);
}
if (newService) {
NOAAWeatherAccessory.metrics.serviceRecoveries++;
newService.updateCharacteristic(characteristic, value);
this.logInfo(`Recovered and updated ${service.displayName} service successfully.`);
}
else {
this.logWarn(`Could not match service type for recovery: ${service.displayName}`);
}
}
catch (recoverErr) {
this.logError(`Failed to self-recover service ${service.displayName}:`, recoverErr);
}
}
}
logMetrics() {
this.logInfo(`📊 NOAA Accessory Metrics → Cache Resets: ${NOAAWeatherAccessory.metrics.cacheResets}, ` +
`Cache Write Errors: ${NOAAWeatherAccessory.metrics.cacheWriteErrors}, ` +
`Characteristic Errors: ${NOAAWeatherAccessory.metrics.characteristicErrors}, ` +
`Service Recoveries: ${NOAAWeatherAccessory.metrics.serviceRecoveries}`);
}
logInfo(message) {
this.platform.log.info(this.formatLog(message));
}
logWarn(message, data) {
this.platform.log.warn(this.formatLog(message), data || '');
}
logError(message, data) {
this.platform.log.error(this.formatLog(message), data || '');
}
formatLog(message) {
const now = new Date();
const formattedTime = new Intl.DateTimeFormat('en-US', {
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
hour12: true,
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
}).format(now);
return `[${formattedTime}] ${message}`;
}
}
exports.NOAAWeatherAccessory = NOAAWeatherAccessory;
NOAAWeatherAccessory.metrics = {
cacheResets: 0,
cacheWriteErrors: 0,
characteristicErrors: 0,
serviceRecoveries: 0,
};