Почитал документацию … Не однозначно. Но похоже если порывов нет - то значения нет …
Тогда готовый код ниже.
Создайте новое правило. Добавьте туда код целиком и сохраните файл.
в устройствах должно появиться виртуальное устройство
// Идентификатор аэропорта (ICAO) для запроса METAR
var ids = "URKA";
var weatherURL = 'https://aviationweather.gov/api/data/metar?ids=' + ids + '&format=json';
// Имя виртуального устройства
var d_name = "weatherMetarEOT";
// Создание виртуального устройства с настраиваемыми параметрами и ячейками для отображения
defineVirtualDevice(d_name, {
title: "Weather EOT Calculator",
cells: {
// === БЛОК НАСТРАИВАЕМЫХ КОНСТАНТ (коэффициенты формулы) ===
const_Smax: {
type: "range",
value: 9.0,
min: 0,
max: 20,
step: 0.1,
units: "°C",
title: { en: "Smax - Max Solar Effect", ru: "Smax - Макс. солнечный эффект" }
},
const_Wb: {
type: "range",
value: 0.18,
min: 0.05,
max: 0.5,
step: 0.01,
units: "°C·с/м",
title: { en: "Wb - Wind Coefficient", ru: "Wb - Ветровой коэффициент" }
},
const_Wt: {
type: "range",
value: 0.1,
min: 0.0,
max: 0.3,
step: 0.01,
title: { en: "Wt - Temperature Wind Factor", ru: "Wt - Темп. фактор ветра" }
},
const_Gf: {
type: "range",
value: 0.3,
min: 0.0,
max: 1.0,
step: 0.05,
title: { en: "Gf - Gust Factor", ru: "Gf - Фактор порывов" }
},
const_Nc: {
type: "range",
value: 3.0,
min: 0.0,
max: 10.0,
step: 0.5,
units: "°C",
title: { en: "Nc - Night Cooling", ru: "Nc - Ночное охлаждение" }
},
// === БЛОК ВЫВОДА РАСЧЕТНЫХ ПАРАМЕТРОВ (только для чтения) ===
// Исходные данные из METAR
display_T: {
type: "temperature",
value: 0,
readonly: true,
title: { en: "T - Temperature", ru: "T - Температура" }
},
display_Vavg_ms: {
type: "value",
value: 0,
units: "m/s",
readonly: true,
title: { en: "Vavg (m/s)", ru: "Vср (м/с)" }
},
display_Vgust_ms: {
type: "value",
value: 0,
units: "m/s",
readonly: true,
title: { en: "Vgust (m/s)", ru: "Vпорыв (м/с)" }
},
display_C: {
type: "value",
value: 0,
readonly: true,
title: { en: "C - Cloud Cover", ru: "C - Облачность" }
},
// Конечные коэффициенты времени
display_Fday: {
type: "value",
value: 0,
readonly: true,
title: { en: "Fday", ru: "Fдень" }
},
display_Fnight: {
type: "value",
value: 0,
readonly: true,
title: { en: "Fnight", ru: "Fночь" }
},
// Промежуточные расчетные величины
display_Veff: {
type: "value",
value: 0,
units: "m/s",
readonly: true,
title: { en: "Veff", ru: "Vэфф" }
},
display_SolarComponent: {
type: "value",
value: 0,
units: "°C",
readonly: true,
title: { en: "Solar Component", ru: "Солнечная составляющая" }
},
display_WindComponent: {
type: "value",
value: 0,
units: "°C",
readonly: true,
title: { en: "Wind Component", ru: "Ветровая составляющая" }
},
display_NightComponent: {
type: "value",
value: 0,
units: "°C",
readonly: true,
title: { en: "Night Component", ru: "Ночная составляющая" }
},
// ИТОГОВЫЙ РЕЗУЛЬТАТ
result_EOT: {
type: "temperature",
value: 0,
readonly: true,
title: { en: "EOT - Effective Outdoor Temp", ru: "EOT - Эффективная темп." }
},
// === СЛУЖЕБНЫЕ ЭЛЕМЕНТЫ ===
// Кнопка для ручного запроса обновления данных
btn_Update: {
type: "pushbutton",
value: false,
title: { en: "Update Data", ru: "Обновить данные" }
},
// Статус последнего обновления
display_LastUpdate: {
type: "text",
value: "Never",
readonly: true,
title: { en: "Last Update", ru: "Последнее обновление" }
}
}
});
// ===================== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ =====================
// Функция преобразования облачности (строка) в числовой коэффициент (0-1)
function cloudCoverToCoefficient(cover) {
var coverMap = {
"SKC": 0.0, "NSC": 0.0, "FEW": 0.25,
"SCT": 0.5, "BKN": 0.75, "OVC": 0.9, "VV": 1.0
};
return coverMap[cover] !== undefined ? coverMap[cover] : 0.5; // По умолчанию 0.5
}
// Функция определения коэффициентов времени (Fдень, Fночь) на основе местного времени
function getTimeCoefficients(utcTimeString, longitude) {
var reportTimeUTC = new Date(utcTimeString);
// Простой расчет местного времени: 15 градусов долготы = 1 час, восток - плюс
var localTimeOffsetHours = longitude / 15.0;
var localTime = new Date(reportTimeUTC.getTime() + (localTimeOffsetHours * 60 * 60 * 1000));
var localHours = localTime.getUTCHours(); // Используем getUTCHours, т.к. дата уже скорректирована
var Fday = 0.0;
var Fnight = 0.0;
if (localHours >= 10 && localHours < 14) {
Fday = 1.0;
} else if ((localHours >= 8 && localHours < 10) || (localHours >= 14 && localHours < 16)) {
Fday = 0.7;
} else if ((localHours >= 6 && localHours < 8) || (localHours >= 16 && localHours < 18)) {
Fday = 0.3;
} // Иначе остается 0.0
if (localHours >= 18 || localHours < 6) {
Fnight = 1.0;
} // Иначе остается 0.0
return { Fday: Fday, Fnight: Fnight, localHours: localHours };
}
// Функция пересчета скорости ветра из узлов (knots) в м/с
function knotsToMps(knots) {
return knots * 0.514444;
}
// Основная функция расчета EOT
function calculateEOT(metarData) {
// 1. Получаем настраиваемые константы из виртуального устройства
var Smax = dev[d_name + "/const_Smax"];
var Wb = dev[d_name + "/const_Wb"];
var Wt = dev[d_name + "/const_Wt"];
var Gf = dev[d_name + "/const_Gf"];
var Nc = dev[d_name + "/const_Nc"];
// 2. Извлекаем и конвертируем данные из METAR
var T = metarData.temp; // температура в °C
var Vavg_knots = metarData.wspd; // средний ветер в узлах
// Порывы: если есть поле wgst, используем его, иначе берем среднюю скорость
var Vgust_knots = metarData.wgst !== undefined ? metarData.wgst : Vavg_knots;
// Конвертируем узлы в м/с для формулы
var Vavg_ms = knotsToMps(Vavg_knots);
var Vgust_ms = knotsToMps(Vgust_knots);
var C = cloudCoverToCoefficient(metarData.cover);
// 3. Рассчитываем коэффициенты времени (местное время)
var timeCoeff = getTimeCoefficients(metarData.reportTime, metarData.lon);
var Fday = timeCoeff.Fday;
var Fnight = timeCoeff.Fnight;
// 4. Выполняем расчет по формуле EOT
// Vэфф = (1 − Gf) × Vср + Gf × Vпорыв
var Veff = (1 - Gf) * Vavg_ms + Gf * Vgust_ms;
// Компоненты формулы
var SolarComponent = Smax * (1 - C) * Fday;
var WindComponent = Wb * Veff * (1 + Wt * Math.abs(T));
var NightComponent = Nc * (1 - C) * Fnight;
// Итоговый EOT = T + Солнечная - Ветровая - Ночная
var EOT = T + SolarComponent - WindComponent - NightComponent;
// 5. Записываем все значения для отображения в виртуальном устройстве
dev[d_name + "/display_T"] = T;
dev[d_name + "/display_Vavg_ms"] = Math.round(Vavg_ms * 10) / 10; // Округление
dev[d_name + "/display_Vgust_ms"] = Math.round(Vgust_ms * 10) / 10;
dev[d_name + "/display_C"] = C;
dev[d_name + "/display_Fday"] = Fday;
dev[d_name + "/display_Fnight"] = Fnight;
dev[d_name + "/display_Veff"] = Math.round(Veff * 10) / 10;
dev[d_name + "/display_SolarComponent"] = Math.round(SolarComponent * 10) / 10;
dev[d_name + "/display_WindComponent"] = Math.round(WindComponent * 10) / 10;
dev[d_name + "/display_NightComponent"] = Math.round(NightComponent * 10) / 10;
dev[d_name + "/result_EOT"] = Math.round(EOT * 10) / 10;
// Обновляем время последнего запроса
var now = new Date();
dev[d_name + "/display_LastUpdate"] = now.toLocaleTimeString();
log("EOT calculated: " + EOT.toFixed(1) + " °C");
}
// ===================== ПРАВИЛА И ЛОГИКА =====================
// Правило для обработки нажатия кнопки "Обновить данные"
defineRule({
whenChanged: d_name + "/btn_Update",
then: function (newValue, devName, cellName) {
if (newValue) {
// Сбрасываем кнопку обратно
dev[d_name + "/btn_Update"] = false;
log("Запрашиваю METAR данные для " + ids);
// Выполняем запрос к API Aviation Weather
runShellCommand("wget -qO- '" + weatherURL + "'", {
captureOutput: true,
exitCallback: function (exitCode, capturedOutput) {
if (exitCode === 0) {
try {
var data = JSON.parse(capturedOutput);
if (data && data.length > 0) {
log("Данные получены для " + data[0].icaoId);
calculateEOT(data[0]); // Передаем первый (и единственный) METAR в функцию расчета
} else {
log("Ошибка: массив данных пуст.");
}
} catch (e) {
log("Ошибка парсинга JSON: " + e);
}
} else {
log("Ошибка выполнения wget: код " + exitCode);
}
}
});
}
}
});
// Инициализация: выполняем первый запрос данных при старте скрипта
log("Скрипт EOT Calculator запущен. Виртуальное устройство создано.");
// Можно раскомментировать строку ниже для автоматического обновления при старте:
// dev[d_name + "/btn_Update"] = true;
// Правило для автоматического периодического обновления (например, каждые 10 минут)
// Для активации уберите комментарий и настройте интервал (в миллисекундах)
/*
setInterval(function() {
log("Автоматическое обновление данных METAR");
dev[d_name + "/btn_Update"] = true;
}, 10 * 60 * 1000); // 10 минут
*/