Добрый день.
Писал не сам, а мучал ИИ, но вроде получилось очень да же не плохо
Основные возможности
Точный расчет солнечных времен по координатам (на основе алгоритма SunCalc)
Автоматическая адаптация к сезонным изменениям длины дня
Поддержка часовых поясов - легко перенастраивается при переезде
Приоритет ручного управления - выключатели имеют высший приоритет
Визуальный мониторинг - все параметры доступны через виртуальное устройство
Надежность - не зависит от интернета и внешних сервисов
Виртуальное устройство sun_times :
- Время восхода (формат HH:MM)
- Время заката (формат HH:MM)
- Время последнего обновления
- Кнопка ручного обновления
Особенности реализации
Точность расчетов
Скрипт использует проверенный астрономический алгоритм, учитывающий:
- Эксцентриситет орбиты Земли
- Наклон земной оси (23.44°)
- Атмосферную рефракцию (-0.833°)
- Географические координаты
Надежность
- Резервный алгоритм при ошибках расчета
- Защита от некорректных данных
- Подробное логирование для диагностики
- Автовосстановление при сбоях
Производительность
- Расчет выполняется 1 раз в сутки
- Время хранится в виртуальном устройстве
- Быстрое сравнение времени в минутах
// Настройки
var LATITUDE = 55.26535; // Ваши координаты
var LONGITUDE = 37.74975; // Ваши координаты
var TIMEZONE_OFFSET = 3; // Часовой пояс (MSK = UTC+3)
// Обновлённое виртуальное устройство с кнопкой
defineVirtualDevice("sun_times", {
title: "Время восхода и заката",
cells: {
sunrise: {
title: "Время восхода",
type: "text",
value: "06:00",
readonly: true
},
sunset: {
title: "Время заката",
type: "text",
value: "18:00",
readonly: true
},
last_updated: {
title: "Последнее обновление",
type: "text",
value: "не обновлялось",
readonly: true
},
update_button: {
title: "Обновить сейчас",
type: "pushbutton"
}
}
});
// Функция для преобразования времени в минуты (для сравнения)
function timeToMinutes(timeStr) {
try {
var parts = timeStr.split(':');
if (parts.length !== 2) {
throw new Error("Неверный формат времени: " + timeStr);
}
var hours = parseInt(parts[0], 10);
var minutes = parseInt(parts[1], 10);
if (isNaN(hours) || isNaN(minutes) || hours < 0 || hours > 23 || minutes < 0 || minutes > 59) {
throw new Error("Некорректное время: " + timeStr);
}
return hours * 60 + minutes;
} catch (e) {
log("Ошибка в timeToMinutes: " + e.toString());
return 6 * 60; // Возвращаем 06:00 по умолчанию
}
}
// Функция для красивого форматирования даты и времени (без padStart)
function formatDateTime(date) {
try {
if (!(date instanceof Date) || isNaN(date.getTime())) {
return "неверная дата";
}
var year = date.getFullYear();
var month = date.getMonth() + 1;
var day = date.getDate();
var hours = date.getHours();
var minutes = date.getMinutes();
var seconds = date.getSeconds();
// Ручное добавление ведущих нулей (без padStart)
month = month < 10 ? "0" + month : month;
day = day < 10 ? "0" + day : day;
hours = hours < 10 ? "0" + hours : hours;
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
return year + "-" + month + "-" + day + " " + hours + ":" + minutes + ":" + seconds;
} catch (e) {
log("Ошибка в formatDateTime: " + e.toString());
return "ошибка формата";
}
}
// Функция для форматирования времени в HH:MM (без padStart)
function formatTime(date) {
try {
if (!(date instanceof Date) || isNaN(date.getTime())) {
log("formatTime: date не является Date объектом");
return "06:00";
}
var hours = date.getHours();
var minutes = date.getMinutes();
// Ручное добавление ведущих нулей
var hoursStr = hours < 10 ? "0" + hours : "" + hours;
var minutesStr = minutes < 10 ? "0" + minutes : "" + minutes;
return hoursStr + ":" + minutesStr;
} catch (e) {
log("Ошибка в formatTime: " + e.toString());
return "06:00";
}
}
// Функция для получения локального времени
function getLocalTime() {
var now = new Date();
return new Date(now.getTime() + TIMEZONE_OFFSET * 60 * 60 * 1000);
}
// Точная функция расчета солнечных времен на основе SunCalc
function calculateSunTimes(date, lat, lng, timezoneOffset) {
log("Точный расчет солнечных времен");
try {
// Константы
var rad = Math.PI / 180;
var dayMs = 1000 * 60 * 60 * 24;
var J1970 = 2440588;
var J2000 = 2451545;
// Вспомогательные функции
function toJulian(date) {
return date.valueOf() / dayMs - 0.5 + J1970;
}
function fromJulian(j) {
return new Date((j + 0.5 - J1970) * dayMs);
}
function toDays(date) {
return toJulian(date) - J2000;
}
function rightAscension(l, b) {
var e = rad * 23.4397;
return Math.atan2(Math.sin(l) * Math.cos(e) - Math.tan(b) * Math.sin(e), Math.cos(l));
}
function declination(l, b) {
var e = rad * 23.4397;
return Math.asin(Math.sin(b) * Math.cos(e) + Math.cos(b) * Math.sin(e) * Math.sin(l));
}
function solarMeanAnomaly(d) {
return rad * (357.5291 + 0.98560028 * d);
}
function eclipticLongitude(M) {
var C = rad * (1.9148 * Math.sin(M) + 0.02 * Math.sin(2 * M) + 0.0003 * Math.sin(3 * M));
var P = rad * 102.9372;
return M + C + P + Math.PI;
}
function sunCoords(d) {
var M = solarMeanAnomaly(d);
var L = eclipticLongitude(M);
return {
dec: declination(L, 0),
ra: rightAscension(L, 0)
};
}
var J0 = 0.0009;
function julianCycle(d, lw) {
return Math.round(d - J0 - lw / (2 * Math.PI));
}
function approxTransit(Ht, lw, n) {
return J0 + (Ht + lw) / (2 * Math.PI) + n;
}
function solarTransitJ(ds, M, L) {
return J2000 + ds + 0.0053 * Math.sin(M) - 0.0069 * Math.sin(2 * L);
}
function hourAngle(h, phi, d) {
var cosH = (Math.sin(h) - Math.sin(phi) * Math.sin(d)) / (Math.cos(phi) * Math.cos(d));
if (cosH > 1) return 0;
if (cosH < -1) return Math.PI;
return Math.acos(cosH);
}
function getSetJ(h, lw, phi, dec, n, M, L) {
var w = hourAngle(h, phi, dec);
var a = approxTransit(w, lw, n);
return solarTransitJ(a, M, L);
}
var times = [
[-0.833, 'sunrise', 'sunset']
];
var lw = rad * -lng;
var phi = rad * lat;
var d = toDays(date);
var n = julianCycle(d, lw);
var ds = approxTransit(0, lw, n);
var M = solarMeanAnomaly(ds);
var L = eclipticLongitude(M);
var dec = declination(L, 0);
var Jnoon = solarTransitJ(ds, M, L);
var result = {
solarNoon: fromJulian(Jnoon),
nadir: fromJulian(Jnoon - 0.5)
};
for (var i = 0; i < times.length; i++) {
var time = times[i];
var h0 = time[0] * rad;
var Jset = getSetJ(h0, lw, phi, dec, n, M, L);
var Jrise = Jnoon - (Jset - Jnoon);
result[time[1]] = fromJulian(Jrise);
result[time[2]] = fromJulian(Jset);
}
// Корректируем на часовой пояс
var offsetMs = timezoneOffset * 60 * 60 * 1000;
var sunriseLocal = new Date(result.sunrise.getTime() + offsetMs);
var sunsetLocal = new Date(result.sunset.getTime() + offsetMs);
log("Точный расчет:");
log("Восход: " + formatDateTime(sunriseLocal));
log("Закат: " + formatDateTime(sunsetLocal));
return {
sunrise: sunriseLocal,
sunset: sunsetLocal
};
} catch (e) {
log("Ошибка в точном calculateSunTimes: " + e.toString());
// Возвращаем значения по умолчанию при ошибке
var defaultTime = new Date(date);
var sunrise = new Date(defaultTime);
var sunset = new Date(defaultTime);
sunrise.setHours(6, 0, 0, 0);
sunset.setHours(18, 0, 0, 0);
return {
sunrise: sunrise,
sunset: sunset
};
}
}
// Функция для обновления времени восхода и заката
function updateSunTimes() {
log("Начало updateSunTimes");
try {
var localTime = getLocalTime();
log("Локальное время: " + formatDateTime(localTime));
var times = calculateSunTimes(new Date(), LATITUDE, LONGITUDE, TIMEZONE_OFFSET);
log("Времена получены");
if (!times || !times.sunrise || !times.sunset) {
throw new Error("calculateSunTimes не вернул корректные данные");
}
var sunriseStr = formatTime(times.sunrise);
var sunsetStr = formatTime(times.sunset);
var updateTimeStr = formatDateTime(localTime);
log("Форматированные времена: " + sunriseStr + " / " + sunsetStr);
dev["sun_times/sunrise"] = sunriseStr;
dev["sun_times/sunset"] = sunsetStr;
dev["sun_times/last_updated"] = updateTimeStr;
log("Успешно обновлено время восхода/заката: " + sunriseStr + " / " + sunsetStr);
} catch (e) {
log("Ошибка в updateSunTimes: " + e.toString());
var localTime = getLocalTime();
dev["sun_times/sunrise"] = "06:00";
dev["sun_times/sunset"] = "18:00";
dev["sun_times/last_updated"] = "Ошибка: " + formatDateTime(localTime);
}
}
// Правила обновления времени восхода и заката
defineRule("update_sun_times_on_start", {
asSoonAs: function() {
return true;
},
then: function() {
log("Первоначальное обновление времени восхода/заката");
updateSunTimes();
}
});
defineRule("update_sun_times_daily", {
when: cron("0 0 * * *"), // Каждый день в 00:00
then: function() {
log("Ежедневное обновление времени восхода/заката");
updateSunTimes();
}
});
// Ручное обновление по кнопке в виртуальном устройстве
defineRule("manual_sun_times_update", {
when: function() {
return dev["sun_times/update_button"];
},
then: function() {
log("Ручное обновление времени восхода/заката");
updateSunTimes();
}
});
Мне собственно нужно было получить ночное время, вот пример функции
// Функция определения ночного времени с правильным сравнением
function isNight() {
try {
var localTime = getLocalTime();
var currentTime = formatTime(localTime);
var currentMinutes = timeToMinutes(currentTime);
var sunrise = dev["sun_times/sunrise"] || "06:00";
var sunset = dev["sun_times/sunset"] || "18:00";
var sunriseMinutes = timeToMinutes(sunrise);
var sunsetMinutes = timeToMinutes(sunset);
// Правильное сравнение времени в минутах
var isNightTime;
if (sunriseMinutes < sunsetMinutes) {
// Нормальный случай: восход до заката
isNightTime = currentMinutes < sunriseMinutes || currentMinutes >= sunsetMinutes;
} else {
// Экзотический случай: восход после заката (полярная ночь/день)
isNightTime = currentMinutes < sunriseMinutes && currentMinutes >= sunsetMinutes;
}
log("Текущее время: " + currentTime + " (" + currentMinutes + " мин)" +
", Восход: " + sunrise + " (" + sunriseMinutes + " мин)" +
", Закат: " + sunset + " (" + sunsetMinutes + " мин)" +
", Ночь: " + isNightTime);
return isNightTime;
} catch (e) {
log("Ошибка в isNight(): " + e.toString());
// Резервный вариант
var hours = getLocalTime().getHours();
return hours < 6 || hours >= 18;
}
}
Всем удачи)