Протокол DALI

Подскажите, когда наступит этот долгожданный момент? Сейчас на тесте есть этот шлюз. Хочу использовать его в текущем проекте. Но реализация из темы Приведение значений в шаблонах к нужному диапазону - #9 от пользователя Explorerol явно “хромает”. Может можно поучаствовать в тестировании, дал бы обратную связь.

Думаю, что недели через две можно будет что-нибудь потестировать. Сейчас собираем оборудование для тестового стенда.

Тогда может подскажете, как “выровнять” значения получаемые от шлюза?
image
в шаблоне так прописанно:

{
			"name" : "A0_valRead",
			"reg_type" : "holding",
			"address" : 3004,
			"type": "dimmer",
			"scale" : 0.583,
			"offset" : -48,
			"format": "u16"	
		},

Вот это, думаю, не правильно. Варианты type описаны здесь:

Попробовал так:

{
			"name" : "A0_valRead",
			"reg_type" : "holding",
			"address" : 3004,
			"type": "range",
      "max": 100,
      "min": 0,
			"scale" : 0.583,
			"offset" : -48,
			"format": "u16"	
		},

Стало только хуже:
image

Секундочку, так вы же его и использовали в теме Приведение значений в шаблонах к нужному диапазону - #9 от пользователя Explorerol

Там я приводил пример, как задать параметр poll_interval через шаблон. Остальная часть была взята из шаблона пользователя: да, не доглядел.

Так в итоге, как можно сделать, что бы значения целые приходили?

Целые значения с коэффициентом 0.583? Немного непонятно, как с ним могут быть целые.

Ну может округление какое ни будь. Яндексу и Сири все равно на эти коэффициенты, они хотят целые числа от 0 до 100.

Округлить - можно и скриптом, ну и отдать внешнему сервису значения из виртуального устройства.

Но и в самом шаблоне тоже предусмотрен механизм, отлично описано тут:

Спасибо, в таком виде отрабатывает хорошо, за исключением выключение

"name" : "Channel 00",
			"reg_type" : "holding",
			"address" : 3004,
			"type": "value",
			"max": 100,
			"min": 0,
			"round_to": 1,
			"scale" : 0.583,
			"offset" : -48,
			"readonly" : true,
			"format": "u16"

Не подскажете, как лучше реализовать передачу нуля в устройство?

Попробуйте использовать:

"off_value": 0,

Попробовал, при 0 свет все равно горит (хоть и на минимальной яркости).

Еще обнаружил проблему в виде “прилета” значений из соседних регистров:
Данные в регистрах 3004 и 3009 (Запрос текущего уровня яркости индивидуального устройства) - “пляшут”. Мне кажется, они подтягиваются из “соседнего” регистра, т.е. если у меня нулевой светильник (адрес 3004) установлен на 23, а первый (адрес 3009) на 5, То в нулевой иногда прилетает 5, а держится 23. То же самое и с первым, только у него установлено 5, но иногда прилетает 23. Не сталкивались с таким поведением?


а если читаю этот объект через modbus_client --debug -mrtu -b19200 -pnone -s1 /dev/ttyRS485-2 -a21 -t0x03 -r3004 то в ответ всегда приходит 0x00a0 (160 в десятичном), что верно. Тогда мне не понятно, почему wb-mqtt-serial получает другие, соседние значения??

Интересное наблюдение, как только убрал

"max": 100,
"min": 0,
"round_to": 1,
"scale" : 0.583,
"offset" : -48,

проблема с считыванием соседних значений пропала. Может подскажите, как с помощью правила можно создать виртуальное устройство, которое будет равно 0-100, и при изменении своего значения - будет отправлять в реальный адрес шлюза данные с учетом того что:
0=0
1-86=1%
254=100%

Спасибо за помощь (нет), добился управления в фэб интерфейсе WB. Но теперь другая проблема. Не происходит сработка правила, если значение вирт устройства меняется через MQTT. Пробовал и с /on и напрямую в топик. В интерфейсе значение меняется, а правило не срабатывает. Подскажите, как решить проблему??

var hkmin = 0;
var hkmax = 100;
var dalimin = 86;
var dalimax = 254;

var p;

defineRule("simple_folow", {
  whenChanged: "dimmer2/value",
  then: function (newValue, devName, cellName) {
	  log.info("Вход в функцию"); 
    log.info(p); 
    if(newValue > hkmin && newValue <= hkmax){
        p = parseInt(((newValue-hkmin) / (hkmax-hkmin)) * (dalimax-dalimin) + dalimin, 10);
        dev["dali_gw2_21"]["Channel 0"] = p;
    }
  }
});

кусок кода, который “слушает” значение в устройстве /devices/dimmer2/controls/value

Тут нет виртуального устройства. Я создал и попробовал воспроизвести
Не воспроизводится. Правило отрабатывает. Дайте скрипт полный, воспроизводящий проблему.

пока вышел из положения вот таким вот костылём:

var hkmin = 0;
var hkmax = 100;
var dalimin = 86;
var dalimax = 254;

var p;

trackMqtt("/devices/dimmer2/controls/value", function(message){
  log.info("name: {}, value: {}".format(message.topic, message.value));
  log.info(p);
  if(message.value > hkmin && message.value <= hkmax){
    p = parseInt(((message.value-hkmin) / (hkmax-hkmin)) * (dalimax-dalimin) + dalimin, 10);
        dev["dali_gw2_21"]["Channel 0"] = p;
    } else {
      dev["dali_gw2_21"]["Channel 0"] = 0;
    }
});

Весь скрипт выглядит так:

defineVirtualDevice("dimmer2", { //Создаём виртуальнное устройство Диммер3
    title: "dimmer control2",
    cells: {  //с параметрами 
        change: {
            type: "switch", //  тип, публикуемый в MQTT-топике /devices/.../controls/.../meta/type для данного параметра.
            value: false    // значение по умолчанию
        },
		click: {
            type: "switch", //  одиночный клик
            value: false    // значение по умолчанию
        },
		doubleClick: {
            type: "switch", //  двойной клик
            value: false    // значение по умолчанию
        },
        value: {
            type: "value",  // тип (.../meta/type)
            value: 0        // значение по умолчанию
        },
        saved: {
            type: "value",  // тип (.../meta/type)
            value: 100      // значение по умолчанию
        },
    }
});

var dirct3 = false;
var lastdirct3 = false;
var timerLast3;
var timerTime3;
var countClick3 = 0;		// кол-во сделанных кликов (счетчик, начальное значение всегда = 0)

// для тонкой настройки
var timeClick3 = 900;   // время одного клика/длинного клика
var timeout23 = 1500; 	// максимальное время ожидания двойного клика
var timeout3 = 400; 		// интервал изменения яркости димера в мс
var delta3 = 6;     		// шаг приращения яркости диммера
var startBright3 = 1; 	// начальная яркость при розжиге

var hkmin = 0;
var hkmax = 100;
var dalimin = 86;
var dalimax = 254;

var p;

trackMqtt("/devices/dimmer2/controls/value", function(message){
  log.info("name: {}, value: {}".format(message.topic, message.value));
  log.info(p);
  if(message.value > hkmin && message.value <= hkmax){
    p = parseInt(((message.value-hkmin) / (hkmax-hkmin)) * (dalimax-dalimin) + dalimin, 10);
        dev["dali_gw2_21"]["Channel 0"] = p;
    } else {
      dev["dali_gw2_21"]["Channel 0"] = 0;
    }
});


defineRule("simple_folow", {
  whenChanged: "dimmer2/value",
  then: function (newValue, devName, cellName) {
	  log.info("Вход в функцию"); 
    log.info(p); 
    if(newValue > hkmin && newValue <= hkmax){
        p = parseInt(((newValue-hkmin) / (hkmax-hkmin)) * (dalimax-dalimin) + dalimin, 10);
        dev["dali_gw2_21"]["Channel 0"] = p;
    }
  }
});

defineRule("oneClick3", {
    asSoonAs: function() {
		// было одно нажатие, восстанавливаем запомненое значение или полностью выключаем
        return dev["dimmer2/click"];
    },
    then: function() { 						// выполняется при срабатывании правила
        if (dev["dimmer2/value"] > 0) {   	// если свет включен, сохраняем текущее значение и выключаем
			dev["dimmer2/saved"] = dev["dimmer2/value"];
			dev["dimmer2/value"] = 0;
			dev["dali_gw2_21"]["Channel 0"] = 0;
			log.info("Выключить свет");
		} else { 							// если свет выключен, выставляем значение взяв из сохраненного
			dev["dimmer2/value"] = dev["dimmer2/saved"];
			log.info("Включить свет");
		}
		dev["dimmer2/click"] = false; 		// сброс состояния
		log.info("oneClick");
    }
});

defineRule("wtoClick3", {
    asSoonAs: function() {
		// было двойное нажатие, свет на максимум
        return dev["dimmer2/doubleClick"];
    },
    then: function() { 						// выполняется при срабатывании правила
        dev["dimmer2/value"] = 100; 		// максимальная яркость
		dev["dimmer2/doubleClick"] = false; // сброс состояния
		log.info("doubleClick");
    }
});

// правило с именем startClicking
defineRule("startClicking3", {
    asSoonAs: function() {
		// edge-triggered-правило - выполняется, только когда значение
		// данной функции меняется и при этом становится истинным
		// т.е. когда dimmer2/change стало = true, а до этого было fasle
		// запускаем работу диммера по нажатию клавиши
        return dev["dimmer2/change"];
    },
    then: function() { 						// выполняется при срабатывании правила
      //log.info("1 - dirct3:", dirct3, "lastdirct3:", lastdirct3);
        if (dev["dimmer2/value"] == 100) {
            dirct3 = false;
        } else if (dev["dimmer2/value"] == 0) {
            dirct3 = true;
        } else {
            dirct3 = !lastdirct3;
        }
        //log.info("2 - dirct3:", dirct3, "lastdirct3:", lastdirct3);
        startTicker("clickTimer3", timeout3); // запустить таймер clickTimer со срабатыванием каждые N мс
		log.info("startClicking3");
    }
});

defineRule("stopClicking3", {
    asSoonAs: function() {
		// т.е. когда dimmer2/change стало = false, а до этого было true
		// по простому кнопку димера отпустили, прекращаем димировать (тормозим тикающий таймер)
        return !dev["dimmer2/change"];
    },
    then: function() {
        timers.clickTimer3.stop(); // остановить таймер clickTimer
        lastdirct3 = dirct3;
		log.info("stopClicking3");
    }
});

defineRule("doClick3", {
    when: function() {
	// т.е. пока таймер clickTimer3 работает, вызываем функцию при каждом срабатывании таймера 
	// тут работает диммер
        return timers.clickTimer3.firing;
    },
    then: function() {
		log.info("doClick3");
        if (dirct3) { // розжиг
			log.info("розжиг");
            if (dev["dimmer2/value"] >= 93) {
                dev["dimmer2/value"] = 100;
            } else {
				// при начале розжига проверяем на минимальный порог
				// если меньше, то сразу устанавливаем значение яркости = startBright3
				// иначе наращиваем с заданным шагом
				log.info("Start. value =", dev["dimmer2/value"], "startBright3 = ", startBright3)
				if (dev["dimmer2/value"] >= startBright3){
					dev["dimmer2/value"] = ((dev["dimmer2/value"] * 1) + delta3); //.toFixed(0);
				} else {
					dev["dimmer2/value"] = startBright3; //.toFixed(0);
				}
				log.info("Finish. value =", dev["dimmer2/value"])
            }
        } 
		if (!dirct3) { // затухание
			log.info("затухание");
            if (dev["dimmer2/value"] <= 6) {
                dev["dimmer2/value"] = 0;
            } else {
                dev["dimmer2/value"] = ((dev["dimmer2/value"] * 1) - delta3);//.toFixed(0);
            }
        }
    }

});

defineRule("timeout23", {
    when: function() {
		//вызываем функцию при срабатывании таймера 
        return timers.doubleClickTimeout3.firing;
    },
    then: function() {
		log.info("Прошло много времени, сбрасываем счетчик");
		countClick3 = 0;
	}
});

defineRule("long_press3", {
	// Срабатывание при изменении значения параметра.
    whenChanged: "wb-gpio/A2_IN",  //при изменении состояния A2_IN у устройства wb-gpio
    then: function(newValue, devName, cellName) { //выполняй следующие действия
		log.info("newValue = ", newValue);
        if (newValue) {
            timerLast3 = Date.now();
			log.info("timerLast3 = ", timerLast3);
			startTimer("doubleClickTimeout3", timeout23); // старт таймера ожидания двух кликов
            dev["dimmer2/change"] = true;
        } else {
            dev["dimmer2/change"] = false;
            timerTime3 = Date.now();
			log.info("timerTime3 = " + timerTime3);
            if ((timerTime3 - timerLast3) <= timeClick3) {
				log.info("countClick3 = ", countClick3);
				if (countClick3 >= 1){
					log.info("Было второе короткое нажатие");
					dev["dimmer2/doubleClick"] = true;
					countClick3 = 0; //сброс счетчика
				}
				else if (countClick3 <= 0){
					log.info("Было короткое нажатие");
					dev["dimmer2/click"] = true;
					countClick3 = countClick3 + 1;
				}
            } else if ((timerTime3 - timerLast3) >= timeClick3) {
				countClick3 = 0; //сброс счетчика
				lastdirct3 = dirct3;
				timers.doubleClickTimeout3.stop(); // остановить таймер, т.к. было длинное нажатие
                log.info("было длинное нажатие");
            }
        }
    }
});

Если управлять через WEB работает нормально, но если управлять через A2_IN - работает не стабильно. Завтра продолжу эксперименты, Если подскажите способ оптимизировать правило, или всю задачу - буду признателен.

2 лайка