You've got a good question there :-)
Okay, so some basics.
The only commands within the Velbus Protocol to support ${param} are dimmer_level & memo_text (Although the ${param} isn't strictly required, you can put any value in and it will be replaced with the dynamic value)
So a rule with..
then
execute.command( "dimmer01" , 25 );
end
Or
rule "Link button event to memo text"
when
Event( source == "A button Sensor", value == "pressed" )
then
execute.command( "memo_text" ,"Hello world:100");
end
Would work.
If I'm reading your question correctly, you just need a tiny tweak to your current rule to get the result you need.
You will need to set an On command and an OFF command for what you're trying to control...
rule "Link two things"
when
Event( source == "A Sensor", value == "on" )
then
execute.command( "Something ON" );
else
execute.command( "Something OFF" );
end
I have something similar running for a client that watches a Velbus glass panel thermostat (heater status) and switches an IP (http addressable) relay.
rule "Sync VMBGP2 HEATER status"
when
Event( source == "GP2-Heater-Status", value == "pressed" )
then
execute.command( "IP_Relay_Ch1_ON" );
else
execute.command( "IP_Relay_Ch1_OFF" );
end
You could take it a stage further and use a regex query to monitor more than 1 sensor and match part of that sensor name to the executed commands.
For example, you might have 10 sensors
1_sensor
2_sensor
3_sensor
Etc
And 10 pairs of commands
1_ON
1_OFF
2_ON
2_OFF
Etc
To save you writing 10 rules, a single rule would match the unique part of the sensor name to the unique part of the command names.
We have this running where multiple new Velbus thermostats are linked to existing heating control relays.
Does that help you?
Best wishes,
Stuart
I see what you're getting at :-)
The only variable that can be inserted into the dimmer_level command is the dimmer level, not the channel number AND dimmer level.
So if I'm understanding correctly, you want to define the channel number within rules.
The only way to do that is to create multiple commands with a command name with a unique identifier.
Then use something clever in rules to define the command you want, then set a level for that command.
I'll dig out an example rule for you to look at.
So, in your device command you 'must enter something'.
So, I suggest you put.
1:50
So that if the command were to be used with a button it would set the dimmer level to 50%
Also, if the command is used within a slider, the new slider value will overwrite the 50%.
Likewise in a rule the new value from the rule will overwrite the 50%
So this simple rule should work :-
rule "Link two things"
when
Event( source == "A Sensor", value == "on" )
then
execute.command( "Dim to a level", 25 );
//else
// execute.command( "Something OFF" );
end
Richard Turner created a rule that seperates an RGB HEX colour value In-Memory command status (where the value is set by another In-Memory command linked to a colour picker UI element) into 3 seperate % values for use with 3 Velbus dimmer channels.
In-Memory command "186_RGB_Colour_Picker_Set" > linked to Colour Picker UI element
In-Memory status command linked to Sensor "186_RGB_Colour_Picker_Status"
3 seperate Velbus dimmer_level commands
186-4DC-Ch1R-Dim100
186-4DC-Ch2G-Dim100
186-4DC-Ch3B-Dim100
Does this help ?
rule "186-RGB To 3 Channels" when
// SOURCE here is an In-Memory status sensor that shows the RGB HEX value from a colour picker "186_RGB_Colour_Picker_Status"
$evt:Event( source == "186_RGB_Colour_Picker_Status", $val : value)
then
String valStr = $val.toString();
if (valStr != null && valStr.length() == 6) {
// Split a single 6 digit HEX RGB colour value into 3 seperate RGB values (in HEX format)
String rStr = valStr.substring(0,2);
String gStr = valStr.substring(2,4);
String bStr = valStr.substring(4,6);
// Convert 2 digit HEX values into DEC int values
Integer r = Integer.parseInt(rStr, 16);
Integer g = Integer.parseInt(gStr, 16);
Integer b = Integer.parseInt(bStr, 16);
// Convert 0-255 value to 0-100 whole number
r = (int)Math.round(((double)r / 255d) * 100);
g = (int)Math.round(((double)g / 255d) * 100);
b = (int)Math.round(((double)b / 255d) * 100);
execute.command( "Red_Set", r.toString() );. // In-Memory command used as a reference in the UI for debugging
execute.command( "186-4DC-Ch1R-Dim100", r.toString() ); // A channel of a VMB4DC that is used to drive the Red elements of an RGB fixture
execute.command( "Green_Set", g.toString() );. // In-Memory command used as a reference in the UI for debugging
execute.command( "186-4DC-Ch2G-Dim100", g.toString() ); // A channel of a VMB4DC that is used to drive the Green elements of an RGB fixture
execute.command( "Blue_Set", b.toString() ); // In-Memory command used as a reference in the UI for debugging
execute.command( "186-4DC-Ch3B-Dim100", b.toString() ); // A channel of a VMB4DC that is used to drive the Blue elements of an RGB fixture
}
end
Michal took this a step further when I started playing around with the HTTP driven DMX device by creating random HEX values to use asRGB strings.
Where a pulsing status is used to trigger the rule.
(Not the output state of an 'Interval Timer' relay channel in Velbus as this would give a constant state of "timer", I linked the interval timer relay channel to another relay (momentary action) so that the slave relay showed on and off states)
An interval timer in Rules might be a better option, but I wanted to be able to vary the interval pulse time in VelbusLink or use multiple actions wth different times.
This rule sees 1 trigger and creates 6 different RGB HEX values to be sent to 6 seperate SPi pixels, using the same single HTTP command to address the SPi / DMX adapter. Where the %{param} is used to (only) transfer the HEX value, the start pixel and end pixel information.
package org.openremote.controller.protocol;
global org.openremote.controller.statuscache.CommandFacade execute;
import java.util.*;
rule "Ch5 slave interval timer"
when
Event( source == "01_VMB1RYNOS_ch5", value == "on" )
then
java.util.Random randomGenerator = new java.util.Random();
execute.command("DMX_Universal_Uni0",String.format("%06X", randomGenerator.nextInt(0x1000000)) + "&start=2&end=2");
execute.command("DMX_Universal_Uni0",String.format("%06X", randomGenerator.nextInt(0x1000000)) + "&start=3&end=3");
execute.command("DMX_Universal_Uni0",String.format("%06X", randomGenerator.nextInt(0x1000000)) + "&start=4&end=4");
execute.command("DMX_Universal_Uni0",String.format("%06X", randomGenerator.nextInt(0x1000000)) + "&start=5&end=5");
execute.command("DMX_Universal_Uni0",String.format("%06X", randomGenerator.nextInt(0x1000000)) + "&start=6&end=6");
execute.command("DMX_Universal_Uni0",String.format("%06X", randomGenerator.nextInt(0x1000000)) + "&start=7&end=7");
end
It uses a Regex to monitor multiple sensors with command names.
It extracts 2 unique elements in the sensor names and uses those to define which command is executed. ("Zone name" and "Time slot" from "Hall_Seditary_Status-query-01")
It also takes the time value from the sensor, adds 30 minutes and injects that new time value into the executed command.
(I'm not totally sure about how this dark magic works, but I'm very grateful that it does, it reduced 42 rules down to just one)
// Sync Seditary times to Active times with 30 minute offset
rule "Alarm Synchroniser"
when
$evt:Event(source matches "^.*_Seditary_Status-query-\\d+$", $source : source)
then
// The sensor value is the time of the seditary alarm as HH:MM
String strValue = $evt.getValue().toString();
String[] values = strValue.split(":");
if (values.length != 2) {
return;
}
int sensorNumber = 0;
int hours = 0;
int mins = 0;
String alarmPrefix = "";
try {
// Extract the number from the sensor name
Pattern p = Pattern.compile("^(.*)_Seditary_Status-query-(\\d+)$");
Matcher m = p.matcher($source);
m.matches();
alarmPrefix = m.group(1);
sensorNumber = new Integer(m.group(2));
hours = new Integer(values[0]);
mins = new Integer(values[1]);
// Add 30 mins to the time and set this as the time for the active alarm
mins += 30;
execute.command(alarmPrefix + "_Active_Time_Set_" + sensorNumber, hours + ":" + mins);
} catch (Exception e) {
System.out.println("############## Alarm synchroniser exception: " + e.getMessage() + " #################");
return;
}
end
rule "Range Event Command Execution"
when
$evt: Range ( source == "Display Volume", value == 100 )
then
execute.command ("TestMSG", $evt.getValue());
end
I'm really curious to know what you're attempting to do :-)
Michal's PID logic might be on a similar line?
I also think that you need some specialist help here :-)
With luck one of the magicians will step forward and help you to the next stage.
In the mean time, I'm working on a GitHub page with a list of sample rules.
https://github.com/openremote/Documentation/wiki/OpenRemote-Rules-examples---A-work-in-progress
One of the rules at the bottom extracts values, modifies them and injects the new value into a command.
I'll not say I'm even close to understanding this dark magic, so I can only wish you luck ;-)
Good luck.
Stuart
--
You received this message because you are subscribed to the Google Groups "OpenRemote" group.
To unsubscribe from this group and stop receiving emails from it, send an email to openremotecommu...@googlegroups.com.
Visit this group at https://groups.google.com/group/openremotecommunity.
To view this discussion on the web visit https://groups.google.com/d/msgid/openremotecommunity/82d081fd-6b3b-4725-93fc-fe5baf88b1e9%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
To unsubscribe from this group and stop receiving emails from it, send an email to openremotecommunity+unsub...@googlegroups.com.
I'll keep adding rules and explanations to that GitHub page for as long as magicians like Michal and Richard keep dreaming them up :-)
Now, I'm a little curious what you're trying to achieve with this rule.
Are you getting the room temperature from a Velbus glass panel?
Cheers
Stuart
rule "Initialise"
salience 10
then
execute.command("TargetTemp",_ReadFromFile("TargetTemp","20.0\u00B0"));
end
rule "Store values"
timer(int: 2s)
when
(
Event($s:source=="TargetTemp", $v:value, eval(!_ReadFromFile($s,"").equals($v))) ||
)
then
log($s+" old value: "+_ReadFromFile($s,"||")+"; new value:"+$v.toString());
_WriteToFile($s, $v);
end
rule "Target Temp Inc"
timer(int:300ms)
when
Event(source == "TargetTemp", $v: value, eval(_GetTemp(value) < 30))
Event(source == "TargetTempInc" , value == "ON")
then
execute.command("TargetTempInc","off");
execute.command("TargetTempDec","OFF");
execute.command("TargetTemp", _ShiftTemp($v.toString(), 0.5));
end
rule "Thermostat On"
when
Event(source=="TargetTemp", $v: value)
Event(source=="Temp1", value==$v)
then
String s = $v.toString();
Double t = java.lang.Math.max(16.0, Double.parseDouble(s.substring(0,s.length()-1))+0.3); // 0.3 hysteresis
execute.command("Thermostat", "ON" );
end
rule "Thermostat On"
when
Event(source=="TargetTemp", $v: value)
Event(source=="Temp1", eval(_GetTemp($v)-_GetTemp(value)>=0.3)) // 0.3 hysteresis
then
execute.command("Thermostat", "ON" );
end
ERROR 2017-03-13 19:41:36,909 : Error in rule definition 'modeler_rules.drl' : wrong class format
java.lang.RuntimeException: wrong class format
ERROR 2017-03-13 19:41:36,931 : Cannot start event processor 'Drools Rule Engine' : java.lang.ClassNotFoundException: org/openremote/controller/protocol/Rule_Target_Temp_Inc_0DefaultConsequenceInvoker
org.drools.RuntimeDroolsException: java.lang.ClassNotFoundException: org/openremote/controller/protocol/Rule_Target_Temp_Inc_0DefaultConsequenceInvoker
TRACE 2017-03-13 19:48:49,831 : Unable to retrieve controller identity
org.openremote.controller.exception.ConnectionException: The required password for user 'username' was not found. Password manager error : Error accessing users password: {0}
at org.openremote.controller.service.BeehiveCommandCheckService$BeehiveCommandChecker.connect(Unknown Source)
DEBUG 2017-03-15 19:35:25,975 (Drools): File read error: TargetTemp1DEBUG 2017-03-15 19:35:26,134 (Drools): Inserted new event source "TargetTemp2"DEBUG 2017-03-15 19:35:26,135 (Drools): Fact count changed from 9 to 1 on "TargetTemp2"DEBUG 2017-03-15 19:35:26,140 (Drools): Inserted new event source "TargetTemp3"DEBUG 2017-03-15 19:35:26,141 (Drools): Fact count changed from 1 to 2 on "TargetTemp3"DEBUG 2017-03-15 19:35:26,146 (Drools): Inserted new event source "Pin3Status"DEBUG 2017-03-15 19:35:26,147 (Drools): Fact count changed from 2 to 3 on "Pin3Status"DEBUG 2017-03-15 19:35:26,149 (Drools): Inserted new event source "TargetTemp1"DEBUG 2017-03-15 19:35:26,149 (Drools): Fact count changed from 3 to 4 on "TargetTemp1"DEBUG 2017-03-15 19:35:26,184 (Drools): Inserted new event source "Pin0Status"DEBUG 2017-03-15 19:35:26,184 (Drools): Fact count changed from 4 to 5 on "Pin0Status"DEBUG 2017-03-15 19:35:26,185 (Drools): Inserted new event source "Pin2Status"DEBUG 2017-03-15 19:35:26,186 (Drools): Fact count changed from 5 to 6 on "Pin2Status"DEBUG 2017-03-15 19:35:26,935 (Drools): Inserted new event source "Temp2"DEBUG 2017-03-15 19:35:26,936 (Drools): Fact count changed from 6 to 7 on "Temp2"DEBUG 2017-03-15 19:35:26,975 (Drools): Inserted new event source "Temp1"DEBUG 2017-03-15 19:35:26,976 (Drools): Fact count changed from 7 to 8 on "Temp1"DEBUG 2017-03-15 19:35:27,018 (Drools): Inserted new event source "Temp3"DEBUG 2017-03-15 19:35:27,018 (Drools): Fact count changed from 8 to 9 on "Temp3"
rule "Initialise"
salience 10
then
execute.command("TargetTemp1",_ReadFromFile("TargetTemp1","20.0"));
end
function void _WriteToFile(String fn, Object o){
String vl = o.toString();
PrintWriter writer = new PrintWriter(fn+".txt", "UTF-8");
writer.println(vl);
writer.close();
}
function String _ReadFromFile(String fn, String dft){
String result = dft;
try{
BufferedReader fr = new BufferedReader(new InputStreamReader(new FileInputStream(fn+".txt"), "UTF-8"));
try{
result = fr.readLine();
} finally {
fr.close();
}
} catch (IOException e) {
// e.printStackTrace();
log("File read error: "+fn);
}
return(result);
}
function Double _GetTemp(Object o){
String s = o.toString();
try{
if(s.length()>1){
return(Double.parseDouble(s.substring(0, s.length()-1)));
}else{
return(0.0);
}
} catch (NumberFormatException e) {
return(0.0);
}
}
function String _ShiftTemp(Object o, double sh){
String s = o.toString();
try{
Double t = Double.parseDouble(s.substring(0,s.length()-1)) + sh;
return(String.format("%.1f",t));
} catch (NumberFormatException e) {
return("0.0");
}
}
rule "Target Temp Inc"
timer(int:300ms)
when
Event(source == "TargetTemp1Inc" , value == "on")
Event(source == "TargetTemp1", $v: value)
then
execute.command("TargetTemp1Inc","off");
execute.command("TargetTemp1", _ShiftTemp($v.toString(), 0.1));
end
rule "Target Temp Dec"
timer(int:300ms)
when
Event(source == "TargetTemp1Dec" , value == "on")
Event(source == "TargetTemp1", $v: value)
then
execute.command("TargetTemp1Dec","off");
execute.command("TargetTemp1", _ShiftTemp($v.toString(), -0.1));
I've spoken in depth to the programmer who takes care of the Velbus implementation in OpenRemote.
He message is that the error / warning messages in the Velbus log aren't Antony to worry about.
In short, any Velbus packets that aren't understood / needed by OpenRemote will appear in that log as a warning, not an error, just a warning.
I've been seeing those exact same warnings in a few installations and I'm sure they aren't anything to worry about.
So I suspect your issue lays in the rules somewhere.
May I ask which bit of Velbus hardware you're using to pick up the outside temperature?
Cheers,
Stuart
Thank you for the quick reply.
Just to be clear. The problem after (2) in the previous message is different from the problem before the (2). You go into detail about problem after the (2)
To answer that one. I use the VMB1TS which can now be used, now I used the update 1.3 of velbus. So thank you for the reply that I do not have to worry about the two errors ( mentioned after the (2)) .
It is a hardware problem, because when I read the temperature from the software of velbus itself, it says 0,0 degrees. Which is not correct. I have to fix that separately. Maybe a wire is faulty.
If you have also an idea about the problem before the (2), I would be very interesting. It is a problem in the rules I think. Is it maybe a problem about the comma "," and the dot "." ?
Hope to hear from you
Richard
If you're using a Velbus sensor, it's as simple as using a level sensor, rather than a custom one.
From my experience, the level sensor can only handle round numbers.
I hope this helps you.
Best wishes,
Stuart
Or
execute.command("250_02_C_DL_PM",String.format("%d",int(correctedValue)));