Значение виртуального устройства получаемое в скрипте правил отличается от содержащегося в MQTT

Виртуальное устройство:

var Teploluxe = {
  T1_temp: {
      type: 'temperature',
      units: 'deg C',
      precision: 0.1,
      title: 'T1 temperature',
      value: 0,
      order: 8,
  },
  T2_temp: {
      type: 'value',
      units: 'deg C',
      precision: 0.1,
      title: 'T2 temperature',
      value: 0,
      order: 9,
  },
  T3_temp: {
      type: 'value',
      units: 'deg C',
      precision: 0.1,
      title: 'T3 temperature',
      value: 0,
      order: 9,
  },
  T4_temp: {
      type: 'value',
      units: 'deg C',
      precision: 0.1,
      title: 'T4 temperature',
      value: 0,
      order: 9,
  },
};

defineVirtualDevice('Teploluxe2000_MGMNT', {
  title: 'Teploluxe 2000 control',
  cells: Teploluxe,
});

В устройствах:
Screen Shot 2025-01-23 at 19.28.15

в MQTT:
Screen Shot 2025-01-23 at 19.27.35

в другом скрипте получаю совершенно другие значения, включил дебаг:

log.info(dev['Teploluxe2000_MGMNT/T1_temp']);

Вот какое значение получаю:
Screen Shot 2025-01-23 at 19.29.57

Я в замешательстве :frowning:

Данные в этот топик публикуются из баш скрипта.

В документации вот нашел:

В случае внешних устройств новое значение публикуется в топике /devices/.../controls/.../on , а соответствующее значение dev[...] изменится только после получения ответного значения в топике /devices/.../controls/... от драйвера устройства:

Честно говоря не понял, что имеется в виду и когда значение в dev[…] будет соответствовать тому, что туда опубликовано?

PS: через MQTT Explorer отображаются актуальные данные

Здравствуйте.

Покажите, пожалуйста, полностью скрипт, в котором используется топик Teploluxe2000_MGMNT/T1_temp.

Статичные данные (скриншот), я так понял, при логирования из данного скрипта?

Скрипт прикладываю
Статичные данные (скриншот) при логировании из данного скрипта, логирование приделал, тк по анализу данных стало понятно что не отрабатывает коэффициент коррекции по уличной температуре при ее изменении

var CoolingTower_Sensor_T = 'wb-mai6_173/IN 1 N Temperature';
var Outdoor_Sensor_T = 'Teploluxe2000_MGMNT/T1_temp';
var Room_TempSensor = "wb-mai6_173/IN 1 P Temperature";

//Regulation Outputs 0-10v
var CoolingTower_Output_AO = 'wb-mao4_88/Channel 1';
var RoomCooling_Fan_1_Output_AO = 'wb-mao4_88/Channel 2';
var RoomCooling_Fan_2_Output_AO = 'wb-mao4_88/Channel 3';

//Output values
var Regulation_Output = [1000,0,0];

//Settings
// device-ID 0-CoolingTower
var Setpoints = [31,32,33];
var CoolingTower_TempSetpoint = 31.1;
var CoolingTower_TempHystresis = 2.1;

var Voltage_Cooling_Max = [9900,10000,10000];
var Voltage_Cooling_Off = [1060,0,0];

var Cooling_Up_Step = [120,0,0];
var Cooling_Down_Step = [200,0,0];

var Outdor_Factor = [20,0,0];
var Error_Factor = [30,0,0];
var Iterations_Factor_Decrease = [0.5,1,1];
var Iterations_Factor_Increase = [0.9,1,1];

//temp vars
var Outdoor_Temperature = dev[Outdoor_Sensor_T] || 0;

var DV_Iterations_cooling = [0,0,0]

var CoolingTower_Temp_LastCycle = dev['ADC_Temp_Control/CT_TempSensor'] || 0;
var CoolingTower_Temp_Error = 0;

var CoolingTower_Current_Temp = dev[CoolingTower_Sensor_T] || 0;

var CoolingTower_Iterations_cooling =0;

var RoomCooling_Hystresis = 0.2;
var RoomCoolingPlusHystresis = 0;
var RoomCoolingMinusHystresis = 0;

var ADC_Temp_Controls = {
		WinterMode: {
		type: 'switch',
	    title: 'Winter mode',
	    value: false,
	    readonly: true,
	    order: 3,
	},
	CT_AutoMode: {
		type: 'switch',
	    title: 'Cooling Tower Auto on',
	    value: true,
	    readonly: false,
	    order: 4,
	},
	CT_TempSensor: {
	    type: 'value',
	    units: 'deg C',
	    precision: 0.1,
	    title: 'Cooling Tower outlet temperature',
	    value: 0,
	    order: 8,
	    readonly: false,
	},
	CT_Regulation_Output: {
	    type: 'value',
	    units: 'mV',
	    precision: 1,
	    min: 0,
	    max: 10000,
	    title: 'Cooling Tower Regulation output',
	    value: Regulation_Output[0],
	    order: 14,
	    readonly: false,
	},
	CT_Setpoint: {
	    type: 'value',
	    units: 'deg C',
	    precision: 0.1,
	    title: 'Cooling Tower setpoint',
	    value: CoolingTower_TempSetpoint,
	    order: 16,
	    readonly: false,
  	},
  	CT_Hystresis: {
	    type: 'value',
	    units: 'deg C',
	    precision: 0.1,
	    title: 'Cooling Tower Hystresis',
	    value: CoolingTower_TempHystresis,
	    order: 16,
	    readonly: false,
  	},
	RC_AutoMode: {
		type: 'switch',
	    title: 'Room Cooling Auto on',
	    value: true,
	    readonly: false,
	    order: 20,
	},
    RC_TempSensor: {
	    type: 'value',
	    units: 'deg C',
	    precision: 0.1,
	    title: 'Room temperature',
	    value: 0,
	    order: 21,
	    readonly: false,
	},
	RC_Setpoint: {
	    type: 'value',
	    units: 'deg C',
	    precision: 0.1,
	    title: 'Room Cooling setpoint',
	    value: 25,
	    order: 22,
	    readonly: false,
  	},
    RC_Fan_1: {
		type: 'switch',
	    title: 'Room Cooling Fan1 on',
	    value: true,
	    readonly: false,
	    order: 23,
  },
  RC_Fan_2: {
		type: 'switch',
	    title: 'Room Cooling Fan2 on',
	    value: true,
	    readonly: false,
	    order: 24,
  },
};

defineVirtualDevice('ADC_Temp_Control', {
  title: 'ADC temperature control',
  cells: ADC_Temp_Controls,
});

function DownVolt_Cooler(Device_ID, Current_T, Last_T, Setpoint, Hystresis) {
	Outdoor_Temperature = dev[Outdoor_Sensor_T];
	var SetpointMinusHystresis = Setpoint - Hystresis;
	var Temperature_Error = Current_T - Setpoint; 
	if (Current_T > SetpointMinusHystresis) {
		if (Current_T < Last_T) {
			DV_Iterations_cooling[Device_ID]--;
			if (DV_Iterations_cooling[Device_ID] < 1) {DV_Iterations_cooling[Device_ID] = 1;}
			Regulation_Output[Device_ID] -= Math.floor((Cooling_Down_Step[Device_ID] - Outdor_Factor[Device_ID] * Outdoor_Temperature - Error_Factor[Device_ID] * Temperature_Error)/((1/DV_Iterations_cooling[Device_ID])*Iterations_Factor_Decrease[Device_ID]));
		}
		if (Current_T > Last_T) {
			DV_Iterations_cooling[Device_ID]++;
			Regulation_Output[Device_ID] += Math.floor((Cooling_Up_Step[Device_ID] + Outdor_Factor[Device_ID] * Outdoor_Temperature + Error_Factor[Device_ID] * Temperature_Error)/(DV_Iterations_cooling[Device_ID]*Iterations_Factor_Increase[Device_ID]));
		}
	}
	if (Current_T > Setpoint) {DV_Iterations_cooling[Device_ID]=0;}	
	if (Current_T < SetpointMinusHystresis) {
		Regulation_Output[Device_ID] = Voltage_Cooling_Off[Device_ID];
		DV_Iterations_cooling[Device_ID] = 0;
	}
	if (Regulation_Output[Device_ID] > Voltage_Cooling_Max[Device_ID]) {Regulation_Output[Device_ID] = Voltage_Cooling_Max[Device_ID];}
	if (Regulation_Output[Device_ID] < Voltage_Cooling_Off[Device_ID]) {Regulation_Output[Device_ID] = Voltage_Cooling_Off[Device_ID];}
	log.info(dev['Teploluxe2000_MGMNT/T1_temp']);
  //log.info("Outdoor_Temperature: ",Outdoor_Temperature,"Current_T:",Current_T,"Last_T:",Last_T,"Regulation_Output:",Regulation_Output[Device_ID],": ",dev['Teploluxe2000_MGMNT/T1_temp']);
	return Regulation_Output[Device_ID];
}

defineRule("cron_every_1_sec", {
  when: cron("@every 2s"), //also @every 1h30m10s more: https://pkg.go.dev/github.com/robfig/cron#hdr-CRON_Expression_Format
  then: function () {
  	dev['ADC_Temp_Control/CT_TempSensor'] = dev[CoolingTower_Sensor_T];
  	if (dev['ADC_Temp_Control/CT_AutoMode']) {
  		dev['ADC_Temp_Control/CT_Regulation_Output'] = DownVolt_Cooler(0, dev[CoolingTower_Sensor_T], CoolingTower_Temp_LastCycle, dev['ADC_Temp_Control/CT_Setpoint'], dev['ADC_Temp_Control/CT_Hystresis'])
      //log.info("T-Error:",CoolingTower_Temp_Error.toFixed(2),"  T-Outdor:",Outdoor_Temperature.toFixed(2),"  Setp-Hyst:",CoolingTowerSetpointMinusHystresis.toFixed(2),"  Curr-T:",CoolingTower_Current_Temp.toFixed(2),"  CoolingTower_Regulation_Output:",Math.floor(CoolingTower_Regulation_Output));
  }
    CoolingTower_Temp_LastCycle = dev['ADC_Temp_Control/CT_TempSensor'];
    dev[CoolingTower_Output_AO] = dev['ADC_Temp_Control/CT_Regulation_Output'];
  },
});

Откуда вы пишете в виртуальное устройство?


Проверим, в скрипте ли проблема: Добавьте еще несколько логов на разные этапы алгоритма:

log.info("Инициализация: ", dev[‘Teploluxe2000_MGMNT/T1_temp’]);

log.info("start function DownVolt_Cooler: ", dev[‘Teploluxe2000_MGMNT/T1_temp’]);

и т.д.

пишу из баш скрипта

 mosquitto_pub -t "/devices/Teploluxe2000_MGMNT/controls/$RegisterName" -m "$float_value"

Проверим, в скрипте ли проблема: Добавьте еще несколько логов на разные этапы алгоритма:

Добрый день.

Значит, дело не в скрипте.

wb-rules, при чтении топика /devices/.../controls/..., видит значения, опубликованные в /devices/.../controls/.../on. Значит, важно записывать значения именно в /on.
Второй важный момент: в описании контрола должен быть прописан параметр readonly: false, иначе изменения из сторонних средств записаны не будут.

Добрый!

Это я еще могу понять.

А это уже похоже на баг, а не на фичу, при установленном readonly: false данные в виртуальном устройстве отображаются корректно, в статистике данные есть, но в скрипте значение этого устройства откуда то берется, но не соответствует реальности. :open_mouth:

Попробуйте, пожалуйста, производить запись в /devices/.../controls/.../on.

Нет. В топики с rteadonly может публиковать только тот процесс который описан в /meta/driver этого топика.

1 лайк

Но, при этом:

  1. mosquitto_pub данные в топик с readonly публикует
  2. эти данные доступны и отображаются в виртуальном устройстве, в истории, в каналах MQTT и в MQTT Explorer

И только в скрипте мы в таком случае не получаем, да нет же, тоже получаем какие-то данные, которые не соответствую текущим, но откуда-то взялись.

Как минимум выглядит не очень логично и очень не прозрачно.

Логика тут вполне простая. Команду на переключение реле (смену якрости в диммере…) надо писать в отдельный топик. Иначе, если команду писать в топик, соответствующий состоянию реле, то все, кто подписан на этот топик, получат сообщение, что реле переключилось, хотя это может быть не так. Например, у WB-MRWM2 есть таймауты, которые модуль выдерживает перед переключением реле. Топик /on и есть командный топик. Далее, если у контрола виртуального устройства установлен флаг readonly, то wb-rules понимает, что управляет этим топиком только и исключительно он сам, и к MQTT не обращается - пользуется внутренней таблицей состояний. Поэтому в скриптах вы видите последнее значение, установленное wb-rules. Ну а то, что в любой MQTT топик вы сторонними средствами можете записать произвольное значение, и никто вам не помешает - такова природа протокола MQTT.

Я уже так и подумал, что есть еще какая-то таблица состояний - спасибо за разьяснение, все понятно.

Для брокера и “читающих” из топика сервисов нет разницы кто именно туда публикует…

Для брокера и “читающих” из топика сервисов нет разницы кто именно туда публикует…

Вот тут описано: GitHub - wirenboard/wb-rules: Rule engine for Wiren Board
Ну и приведу вольную аналогию: Ситуация когда записывается значение в RO топик - это все равно что отключить спидометр от цепей и “устанавливать” его руками.