Код
// =====================================================
// Виртуальный термостат для управления теплым полом
// Версия: 1.0
// =====================================================
// Конфигурация устройств
var CONFIG = {
// Датчик температуры
temperatureSensor: "wb-m1w2_228/External Sensor 1",
// Реле управления нагревом
heatingRelay: "wb-mrwm2_197/K1",
// Гистерезис температуры (допустимое отклонение)
hysteresis: 0.5,
// Период обновления в секундах
updatePeriod: 10,
// Температура по умолчанию
defaultTemp: 22.0,
// Минимальная и максимальная температура
minTemp: 15.0,
maxTemp: 30.0
};
// Расписание для автоматического режима
// Формат: { день_недели: [ [час, минута, температура], ... ] }
var SCHEDULE = {
// Понедельник - Пятница (рабочие дни)
weekday: [
[6, 30, 23.0], // 06:30 - утренний подогрев
[8, 30, 20.0], // 08:30 - все ушли
[17, 00, 22.0], // 17:00 - вечерний подогрев
[22, 30, 19.0] // 22:30 - спим
],
// Суббота - Воскресенье (выходные)
weekend: [
[8, 00, 23.0], // 08:00 - утренний подогрев
[23, 00, 19.0] // 23:00 - спим
]
};
// Глобальные переменные для хранения состояния
var thermostatState = {
mode: "manual", // "manual" или "auto"
manualSetpoint: CONFIG.defaultTemp,
currentSetpoint: CONFIG.defaultTemp,
currentTemp: null,
isHeating: false,
nextEvent: null,
nextEventTemp: null,
lastUpdateTime: 0
};
// =====================================================
// Описание виртуального устройства
// =====================================================
defineVirtualDevice("floor_thermostat", {
title: "Термостат теплого пола",
cells: {
// Переключатель режима работы
mode: {
type: "switch",
value: false, // false = ручной, true = автоматический
title: "Автоматический режим"
},
// Ползунок для установки температуры в ручном режиме
manual_setpoint: {
type: "range",
value: CONFIG.defaultTemp,
min: CONFIG.minTemp,
max: CONFIG.maxTemp,
title: "Заданная температура (°C)",
readonly: false
},
// Отображение текущей температуры
current_temperature: {
type: "value",
value: 0,
units: "°C",
title: "Текущая температура",
readonly: true
},
// Отображение активной заданной температуры
active_setpoint: {
type: "value",
value: CONFIG.defaultTemp,
units: "°C",
title: "Активная уставка",
readonly: true
},
// Индикатор работы нагрева
heating_state: {
type: "switch",
value: false,
title: "Нагрев",
readonly: true
},
// Следующее событие расписания
next_event: {
type: "text",
value: "---",
title: "Следующее событие",
readonly: true
},
// Температура следующего события
next_event_temp: {
type: "value",
value: 0,
units: "°C",
title: "Температура события",
readonly: true
},
// Статус системы
status: {
type: "text",
value: "Инициализация...",
title: "Статус",
readonly: true
}
}
});
// =====================================================
// Вспомогательные функции
// =====================================================
// Получение текущего дня недели (0 = воскресенье, 6 = суббота)
function getCurrentDayType() {
var day = new Date().getDay();
return (day === 0 || day === 6) ? "weekend" : "weekday";
}
// Форматирование времени
function formatTime(hours, minutes) {
var h = hours < 10 ? "0" + hours : hours;
var m = minutes < 10 ? "0" + minutes : minutes;
return h + ":" + m;
}
// Поиск следующего события в расписании
function findNextScheduleEvent() {
var now = new Date();
var currentHour = now.getHours();
var currentMinute = now.getMinutes();
var dayType = getCurrentDayType();
var schedule = SCHEDULE[dayType];
// Поиск следующего события сегодня
for (var i = 0; i < schedule.length; i++) {
var event = schedule[i];
if (event[0] > currentHour || (event[0] === currentHour && event[1] > currentMinute)) {
return {
time: formatTime(event[0], event[1]),
temp: event[2],
today: true
};
}
}
// Если сегодня событий больше нет, берем первое событие завтра
var tomorrowDay = (now.getDay() + 1) % 7;
var tomorrowDayType = (tomorrowDay === 0 || tomorrowDay === 6) ? "weekend" : "weekday";
// var tomorrowDayType = (now.getDay() === 5) ? "weekend" :
// (now.getDay() === 6) ? "weekday" : dayType;
var tomorrowSchedule = SCHEDULE[tomorrowDayType];
if (tomorrowSchedule.length > 0) {
return {
time: formatTime(tomorrowSchedule[0][0], tomorrowSchedule[0][1]) + " (завтра)",
temp: tomorrowSchedule[0][2],
today: false
};
}
return null;
}
// Получение температуры для текущего времени из расписания
function getScheduleTemperature() {
var now = new Date();
var currentHour = now.getHours();
var currentMinute = now.getMinutes();
var dayType = getCurrentDayType();
var schedule = SCHEDULE[dayType];
var activeTemp = CONFIG.defaultTemp;
// Находим последнее прошедшее событие
for (var i = schedule.length - 1; i >= 0; i--) {
var event = schedule[i];
if (event[0] < currentHour || (event[0] === currentHour && event[1] <= currentMinute)) {
activeTemp = event[2];
break;
}
}
// Если все события в будущем, берем последнее событие вчерашнего дня
if (i < 0) {
var yesterdayDay = (now.getDay() - 1 + 7) % 7;
var yesterdayDayType = (yesterdayDay === 0 || yesterdayDay === 6) ? "weekend" : "weekday";
// var yesterdayDayType = (now.getDay() === 1) ? "weekend" :
// (now.getDay() === 0) ? "weekday" : dayType;
var yesterdaySchedule = SCHEDULE[yesterdayDayType];
if (yesterdaySchedule.length > 0) {
activeTemp = yesterdaySchedule[yesterdaySchedule.length - 1][2];
}
}
return activeTemp;
}
// Управление реле нагрева с гистерезисом
function controlHeating(currentTemp, setpoint) {
if (currentTemp === null || currentTemp === undefined) {
// Если нет данных о температуре, выключаем нагрев для безопасности
dev[CONFIG.heatingRelay] = false;
thermostatState.isHeating = false;
return false;
}
var shouldHeat = false;
if (thermostatState.isHeating) {
// Если нагрев включен, выключаем при достижении уставки + половина гистерезиса
shouldHeat = currentTemp < (setpoint + CONFIG.hysteresis / 2);
} else {
// Если нагрев выключен, включаем при падении ниже уставки - половина гистерезиса
shouldHeat = currentTemp < (setpoint - CONFIG.hysteresis / 2);
}
thermostatState.isHeating = shouldHeat;
dev[CONFIG.heatingRelay] = shouldHeat;
return shouldHeat;
}
// Обновление статуса системы
function updateStatus() {
var mode = thermostatState.mode;
var heating = thermostatState.isHeating ? "ВКЛ" : "ВЫКЛ";
var temp = thermostatState.currentTemp !== null ?
thermostatState.currentTemp.toFixed(1) + "°C" : "---";
var status = "Режим: " + (mode === "auto" ? "Авто" : "Ручной") +
" | Нагрев: " + heating +
" | T: " + temp;
dev["floor_thermostat/status"] = status;
}
// =====================================================
// Правила управления
// =====================================================
// Правило: Переключение режима работы
defineRule("thermostat_mode_switch", {
whenChanged: "floor_thermostat/mode",
then: function(newValue) {
thermostatState.mode = newValue ? "auto" : "manual";
// Изменение доступности ползунка ручной температуры
dev["floor_thermostat/manual_setpoint#readonly"] = Boolean(newValue);
log( "правило thermostat_mode_switch получило значение newValue = ", Boolean(newValue), " типа: ", typeof(Boolean(newValue)) );
// Обновление уставки в зависимости от режима
if (thermostatState.mode === "auto") {
thermostatState.currentSetpoint = getScheduleTemperature();
log.info("Термостат: переключен в автоматический режим");
} else {
thermostatState.currentSetpoint = thermostatState.manualSetpoint;
log.info("Термостат: переключен в ручной режим");
}
dev["floor_thermostat/active_setpoint"] = thermostatState.currentSetpoint;
updateStatus();
}
});
// Правило: Изменение ручной уставки
defineRule("thermostat_manual_setpoint", {
whenChanged: "floor_thermostat/manual_setpoint",
then: function(newValue) {
thermostatState.manualSetpoint = newValue;
if (thermostatState.mode === "manual") {
thermostatState.currentSetpoint = newValue;
dev["floor_thermostat/active_setpoint"] = newValue;
log.debug("Термостат: установлена температура " + newValue + "°C");
}
}
});
// Правило: Чтение температуры с датчика
defineRule("thermostat_read_temperature", {
whenChanged: CONFIG.temperatureSensor,
then: function(newValue) {
if (newValue !== null && newValue !== undefined) {
thermostatState.currentTemp = parseFloat(newValue);
dev["floor_thermostat/current_temperature"] = thermostatState.currentTemp;
// Управление нагревом
var heating = controlHeating(thermostatState.currentTemp, thermostatState.currentSetpoint);
dev["floor_thermostat/heating_state"] = heating;
updateStatus();
}
}
});
// =====================================================
// Периодические задачи
// =====================================================
// Обновление расписания и состояния каждые 10 секунд
setInterval(function() {
// Обновление температуры по расписанию в автоматическом режиме
if (thermostatState.mode === "auto") {
var scheduleTemp = getScheduleTemperature();
if (Math.abs(scheduleTemp - thermostatState.currentSetpoint) > 0.1) {
thermostatState.currentSetpoint = scheduleTemp;
dev["floor_thermostat/active_setpoint"] = scheduleTemp;
log.info("Термостат: температура по расписанию изменена на " + scheduleTemp + "°C");
}
}
// Обновление информации о следующем событии
var nextEvent = findNextScheduleEvent();
if (nextEvent) {
dev["floor_thermostat/next_event"] = nextEvent.time;
dev["floor_thermostat/next_event_temp"] = nextEvent.temp;
} else {
dev["floor_thermostat/next_event"] = "---";
dev["floor_thermostat/next_event_temp"] = 0;
}
// Проверка состояния нагрева
if (thermostatState.currentTemp !== null) {
var heating = controlHeating(thermostatState.currentTemp, thermostatState.currentSetpoint);
dev["floor_thermostat/heating_state"] = heating;
}
updateStatus();
}, CONFIG.updatePeriod * 1000);
// =====================================================
// Инициализация при запуске
// =====================================================
setTimeout(function() {
log.info("Термостат теплого пола: инициализация");
// Установка начальных значений
thermostatState.mode = dev["floor_thermostat/mode"] ? "auto" : "manual";
thermostatState.manualSetpoint = dev["floor_thermostat/manual_setpoint"];
if (thermostatState.mode === "auto") {
thermostatState.currentSetpoint = getScheduleTemperature();
dev["floor_thermostat/manual_setpoint#readonly"] = true;
} else {
thermostatState.currentSetpoint = thermostatState.manualSetpoint;
dev["floor_thermostat/manual_setpoint#readonly"] = false;
}
dev["floor_thermostat/active_setpoint"] = thermostatState.currentSetpoint;
// Попытка прочитать текущую температуру
if (dev[CONFIG.temperatureSensor] !== undefined) {
thermostatState.currentTemp = parseFloat(dev[CONFIG.temperatureSensor]);
dev["floor_thermostat/current_temperature"] = thermostatState.currentTemp;
}
updateStatus();
log.info("Термостат теплого пола: инициализация завершена");
}, 1000);