Нужно создать виртуальное устройство

Нужно виртуальное устройство которое будет содержать EOT (Эффективная наружная температура), наружную температуру, направление ветра, скорость ветра, порывы ветра, температуру точки росы.

Цель: использование EOT для погодозависимого управления температурой в доме.

Нужен расчет EOT на основе данных METAR (авиационный метеорологический код для передачи сводок о фактической погоде на аэродроме). Дом находится недалеко от аэродрома, поэтому разумно использовать не личную метеостанцию, а данные METAR (обновляются раз в 30 минут).

Входные данные. API http://aviationweather.gov/api/data/metar?ids=URKA&format=json Формат может быть и другой (какой удобнее/лучше на wirenboard? GeoJSON, CSV, XML, IWXXM???) Документация API здесь Data API

Формула расчета Effective Outdoor Temperature (EOT):

EOT = T + Smax × (1 − C) × Fдень − Wb × Vэфф × (1 + Wt × |T|) − Nc × (1 − C) × Fночь

Где:
Vэфф = (1 − Gf) × Vср + Gf × Vпорыв

Переменные:
1. T — температура наружного воздуха из METAR (°C)
2. Vср — средняя скорость ветра из METAR (м/с)
3. Vпорыв — скорость порывов ветра из METAR (м/с)
4. C — доля облачности (0-1):
   SKC: 0.0, NSC: 0.0, FEW: 0.25, SCT: 0.5, BKN: 0.75, OVC: 0.9, VV: 1.0
5. Fдень — дневной коэффициент (0-1):
   - 10:00-14:00 местного времени: 1.0
   - 8:00-10:00 и 14:00-16:00: 0.7
   - 6:00-8:00 и 16:00-18:00: 0.3
   - Ночь (18:00-6:00): 0.0
6. Fночь — ночной коэффициент (0-1):
   - Ночь (18:00-6:00): 1.0
   - День: 0.0
7. Smax = 9.0 (°C) — максимальный солнечный эффект
8. Wb = 0.18 (°C·с/м) — ветровой коэффициент
9. Wt = 0.1 — температурный фактор ветра
10. Gf = 0.3 — фактор влияния порывов
11. Nc = 3.0 (°C) — ночное охлаждение

Все коэффициенты (Smax, Wb, Wt, Gf, Nc) настраиваются через интерфейс.

Добрый день.
Пример чтения и разбора json:

//12_18_test_1.js
var weatherURL = 'http://aviationweather.gov/api/data/metar?ids=URKA&format=json'

//wget -qO- 'http://aviationweather.gov/api/data/metar?ids=URKA&format=json'


runShellCommand("wget -qO- '" + weatherURL+"'", {
  captureOutput: true,
      exitCallback: function (exitCode, capturedOutput) {
        log.info("cmd output: " + capturedOutput);
        // Парсим строку JSON
        var data = JSON.parse(capturedOutput);
        log.info("Температура: ", data[0].temp); // Выведет: Температура
        log.info("Видимость: ", data[0].visib); // Выведет: Видимость
        
      }
})

Вывод:


А дальше, в общем, простая математика.

Проблема и с математикой, в коде пока не силен, да и куда это все ложить и прикручивать не знаю. Освоил только правила через графический редактор и пока все.

Пока зима хочу запустить погодозависимое управление, сам если буду разбираться, за зиму не успею (

Попробую позвать знакомых, возможно кто-нибудь сделает.

Буду благодарен

А как получить
Vпорыв — скорость порывов ветра из METAR (м/с)
?
Входные данные. API http://aviationweather.gov/api/data/metar?ids=URKA&format=json не содержат этого параметра.

1. Как обозначаются порывы в METAR?

Порывы ветра указываются сразу после средней скорости ветра с помощью буквы G (от Gust).

  • Пример с порывами: 33012G18MPS — это значит: ветер 330°, средняя скорость 12 м/с, порывы до 18 м/с.

В вашем METAR: 07002MPS — есть только средняя скорость (2 м/с), буква G и дополнительное число отсутствуют. Значит, значительных кратковременных усилений ветра не зафиксировано.

Почитал документацию … Не однозначно. Но похоже если порывов нет - то значения нет …

Тогда готовый код ниже.
Создайте новое правило. Добавьте туда код целиком и сохраните файл.
в устройствах должно появиться виртуальное устройство

// Идентификатор аэропорта (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 минут
*/
1 лайк

Спасибо!

ВСЕ РАБОТАЕТ!