var HA_PREFIX = "homeassistant/cover/";
// ===== КОНФИГУРАЦИЯ ШТОР =====
// Добавляйте новые шторы в этот массив
var CURTAINS = [
{
id: "curtain_3_1",
name: "Штора 3_1",
relay_up: "wb-mr6c_103/Curtain 1 Open",
relay_down: "wb-mr6c_103/Curtain 1 Close",
time_up: 12, // время подъема в секундах
time_down: 12 // время опускания в секундах
},
];
// ===== ОСНОВНОЙ КОД =====
// Хранилище состояния всех штор
var curtainsState = {};
// Инициализация всех штор
CURTAINS.forEach(function(curtain) {
var id = curtain.id;
var name = curtain.name;
// Создаем виртуальное устройство для каждой шторы
defineVirtualDevice(id, {
title: name,
cells: {
position: {
type: "range",
value: 0,
min: 0,
max: 100,
readonly: false
},
state: {
type: "text",
value: "stopped",
readonly: true
}
}
});
// Инициализируем состояние
curtainsState[id] = {
position: 0,
timer: null,
direction: null,
config: curtain
};
// MQTT топики
var cmdTopic = "wb/curtains/" + id + "/set";
var posTopic = "wb/curtains/" + id + "/set_position";
var stateTopic = "wb/curtains/" + id + "/state";
var positionTopic = "wb/curtains/" + id + "/position";
// Отправка discovery в Home Assistant
setTimeout(function(curtainId, curtainName, cmdT, posT, stateT, positionT) {
return function() {
var discovery = {
name: curtainName,
unique_id: "wb_" + curtainId,
device_class: "curtain",
command_topic: cmdT,
position_command_topic: posT,
state_topic: stateT,
position_topic: positionT,
payload_open: "OPEN",
payload_close: "CLOSE",
payload_stop: "STOP",
state_opening: "opening",
state_closing: "closing",
state_stopped: "stopped",
position_open: 100,
position_closed: 0,
optimistic: false,
retain: true
};
publish(HA_PREFIX + curtainId + "/config", JSON.stringify(discovery), 1, {retain: true});
publish(positionT, "0", 1, {retain: true});
publish(stateT, "stopped", 1, {retain: true});
};
}(id, name, cmdTopic, posTopic, stateTopic, positionTopic), 3000 + (CURTAINS.indexOf(curtain) * 1000));
// Функция остановки движения
function stopMovement(curtainId) {
return function() {
var state = curtainsState[curtainId];
if (state.timer) {
clearInterval(state.timer);
state.timer = null;
}
dev[state.config.relay_up] = false;
dev[state.config.relay_down] = false;
state.direction = null;
var newState = state.position === 100 ? "open" : (state.position === 0 ? "closed" : "stopped");
dev[curtainId].state = newState;
var stateTopic = "wb/curtains/" + curtainId + "/state";
var positionTopic = "wb/curtains/" + curtainId + "/position";
publish(stateTopic, newState, 1, {retain: true});
publish(positionTopic, state.position.toString(), 1, {retain: true});
};
}(id);
// Функция начала движения
function startMovement(curtainId, target) {
return function() {
var state = curtainsState[curtainId];
var config = state.config;
target = parseInt(target);
if (isNaN(target)) return;
if (target < 0) target = 0;
if (target > 100) target = 100;
if (target === state.position) return;
if (state.timer) {
clearInterval(state.timer);
state.timer = null;
}
state.direction = target > state.position ? "up" : "down";
if (state.direction === "up") {
dev[config.relay_up] = true;
dev[config.relay_down] = false;
} else {
dev[config.relay_up] = false;
dev[config.relay_down] = true;
}
var movementState = state.direction === "up" ? "opening" : "closing";
dev[curtainId].state = movementState;
var stateTopic = "wb/curtains/" + curtainId + "/state";
publish(stateTopic, movementState, 1, {retain: true});
var speed = (state.direction === "up" ? config.time_up : config.time_down) * 1000 / 100;
state.timer = setInterval(function() {
if (state.direction === "up") {
state.position++;
if (state.position > 100) state.position = 100;
} else {
state.position--;
if (state.position < 0) state.position = 0;
}
dev[curtainId].position = state.position;
var positionTopic = "wb/curtains/" + curtainId + "/position";
publish(positionTopic, state.position.toString(), 1, {retain: true});
if ((state.direction === "up" && state.position >= target) ||
(state.direction === "down" && state.position <= target)) {
var stopFunc = stopMovement(curtainId);
stopFunc();
}
}, speed);
};
}(id);
// Обработка ползунка в WB
defineRule("wb_slider_" + id, {
whenChanged: id + "/position",
then: function(newValue) {
var pos = parseInt(newValue);
var state = curtainsState[id];
if (!isNaN(pos) && Math.abs(pos - state.position) > 2) {
var moveFunc = startMovement(id, pos);
moveFunc();
} else {
dev[id].position = state.position;
}
}
});
// Синхронизация состояния
defineRule("sync_state_" + id, {
when: function() { return true; },
every: 30 * 1000,
then: function() {
var state = curtainsState[id];
var positionTopic = "wb/curtains/" + id + "/position";
var stateTopic = "wb/curtains/" + id + "/state";
publish(positionTopic, state.position.toString(), 1, {retain: true});
var newState = state.position === 100 ? "open" : (state.position === 0 ? "closed" : "stopped");
publish(stateTopic, newState, 1, {retain: true});
}
});
log("Initialized curtain: " + id);
});
log("=================================");
log("Curtain manager loaded - " + CURTAINS.length + " curtains configured");
log("=================================");