This page last changed on Sep 15, 2014 by jules_bike.

I definitely think that OR would benefit from the ability to persist values of in memory switches - especially in the case of a power outage or controller shutdown.

For example I use in memory switches for amplifiers linked to Sonos which works nicely for wake up alarms. However if there is a power cut during the night the controller resets the switches to off and the relevant alarms will not sound.

I realise that this can be achieved by writing values out to a file - I had a look round the forums and no one has written up clear instructions how to do this. I am happy to write a user guide but will need some technical assistance - I am using Windows XP.

Anyone able to help?

Jules

Check here http://mqlservice.net/openremote/2014/09/16/persistent-in-memory-variables/

Posted by aktur at Sep 16, 2014 14:53

Great - is this how it works?

Every second it writes the status to an external file.

When it starts up it checks the current status and if it is different from the external value is uses that.

Posted by jules_bike at Sep 16, 2014 21:22

1s delay is just to debounce the variable. It is frequent that novice users create infinite loops, you would want to save the hard drive from burning in this situation
Nevertheless, values are stored only when they change and their value is stable for at least 1s.
There is no check for different values. When it starts up it always read the value from the file. It recognises the startup by the fact that variables are either initialized to an empty string or value 'status'.

Posted by aktur at Sep 17, 2014 08:58

Hi Michal - almost there but no cigar quite yet! On a restart it is not updating the status of a switch which I have left on.

I've followed your instructions creating a second rules file with your code and I have a .txt file being updated each time I toggle the switch.

The file is called "GVOFFICEMUSIC_STATUS.txt" and holds the value either "on" or "off".

It appears that this file resets itself back to off when I restart openremote.

In designer I have a set of virtual commands
"GVOFFICEMUSIC_STATUS"
"GVOFFICEMUSIC_ON"
"GVOFFICEMUSIC_OFF"

These tie in with a switch sensor
"GVOFFICEMUSIC_STATUS"
which uses the "GVOFFICEMUSIC_STATUS" command

A switch in the interface toggles this sensor.

In my regular rules file I have the following rules will call the actual commands:
"Office Music on"
when
Event(source=="GVOFFICEMUSIC_STATUS", value=="on")
then
execute.command("OFFICE_MUSIC_ON");
end

rule "Office Music off"
when
Event(source=="GVOFFICEMUSIC_STATUS", value=="off")
then
execute.command("OFFICE_MUSIC_OFF");
end

Can you see where I am going wrong?

Posted by jules_bike at Sep 17, 2014 20:38

The sensor should be of type custom, because this one initialises in openremote with empty string which is replaced by value red from the file. Switch sensor initialises with "off" which is also a valid state, therefore overwriting it by value from a file is not feasible.

I don't understand your regular rules file though. What is their purpose? You can execute command directly from switch, can't you?

Posted by aktur at Sep 17, 2014 21:20

So that explains the reseting back to off each time.

I am using a virtual memory sensor and switch because I am switching an Amplifier which has no feedback so the virtual sensor stores the status (on or off) and the rules files executes the actual commands.

Can I use a custom sensor with a switch? I've never tried that.

Posted by jules_bike at Sep 18, 2014 07:11

Can I use a custom sensor with a switch? I've never tried that.

Yes, you can. As long as sensor provides "on" and "off" states it can be used with switches. In fact I use custom sensors for almost everything.

Posted by aktur at Sep 18, 2014 09:03

I've tried changing the virtual sensor into a custom sensor with values "on" and "off" but this seems to break the switch in the interface?

Solved ---- it looks like there is an issue with case sensitivity — I have it working now.

Thanks for your help this looks like a good solution. I will test it out fully and report back.

Posted by jules_bike at Sep 18, 2014 16:07

Hi Michal - I have set up a wake up alarm panel using sliders to set hours and minutes which then match the against the datetime variables to trigger events. I've tried using your function to persist the value of the sliders but although it is creating the relevant text file it still defaults back to zero each time I restart the server.

Is there a way to get this to work? The virtual sensor is a range sensor so I guess that is the issue.

Posted by jules_bike at Jan 28, 2015 08:07

Hi Jules,

perhaps you are right, I've tested this only with custom sensors which init to "" or "status" when linked with in-memory variables. Perhaps range has a different init value. Anyway, I think that you can simply amend the Init rule to the following and it will work with any type of sensor.

rule "Init values"
  salience 100
when
  Event($s:source matches "^GV.*", $v:value)
then
  execute.command($s, _ReadFromFile($s, $v.toString()));
end
Posted by aktur at Jan 28, 2015 10:48

Hi Michal thanks for the response.

Not sure exactly what this does but it does not seem to work - if you press a switch it keeps going back to whatever is in the txt file. For the slider if I put a value in the txt file when I try and move the slider it is fixed on the txt value.

Posted by jules_bike at Jan 28, 2015 16:33

Sorry, my mistake. You want to fire the init only once when starting the controller. Because I use only custom sensors I've put value filter for them to do the init only when they are not intialised first. For range sensor the init value must be different. Therefore, perhaps the simplest solution would be to init your sensor explicitly:

rule "Init values explicit"
  salience 100
then
  execute.command("YourSensorCommandName1", _ReadFromFile("YourSensorCommandName1", ""));
  execute.command("YourSensorCommandName2", _ReadFromFile("YourSensorCommandName2", ""));
  execute.command("YourSensorCommandName3", _ReadFromFile("YourSensorCommandName3", ""));
end
Posted by aktur at Jan 28, 2015 17:36

Do you mean that I keep the "init values" rule for custom sensors and add the other rule for the range sensor?

Posted by jules_bike at Jan 28, 2015 18:04

Probably this is the best approach for now as range sensors are initialised to a valid range value. You can try to refactor it and make sure that "Init values" runs only once per sensor, thus you don't need to put explicit names into rules, but this would require a more sophisticated rule codings. Perhaps, I'll update my blog, when I find some time, with this general approach.

Posted by aktur at Jan 29, 2015 10:58

Thanks Michal that works fine for the virtual range sensor, good timing as my alarm did not go off this morning because of a restart which reset all the values...

Posted by jules_bike at Jan 29, 2015 13:48

when i copy and paste your script in to my rules i get this error below, im using a custom sensor with a switch and variables starting GV in the names

im using OpenRemote-Controller-2.2.0_TTS-Email-Serial

ERROR 2015-02-04 13:15:41,540 : Rule definition 'modeler_rules.drl' could not be deployed. See errors below.
ERROR 2015-02-04 13:15:41,541 : ERR 103 Line 15:0 rule 'rule_key' failed predicate: (validateIdentifierKey(DroolsSoftKeywords.RULE))? in rule
ERROR 2015-02-04 13:15:41,541 : ERR 101 Line 15:8 no viable alternative at input 'org' in rule package
ERROR 2015-02-04 13:15:41,542 : There was an error parsing the rule definition 'modeler_rules.drl' : Could not parse knowledge.
java.lang.IllegalArgumentException: Could not parse knowledge.

Posted by robnas at Feb 04, 2015 12:20

have you restarted the controller? I find that if you don't you get an error.

Here is my drools file with an example virtual switch. You may be missing something in the header...

package org.openremote.controller.protocol
import org.openremote.controller.model.event.*;
global org.openremote.controller.statuscache.CommandFacade execute;
global org.openremote.controller.statuscache.SwitchFacade switches;
global org.openremote.controller.statuscache.LevelFacade levels;
import java.util.concurrent.TimeUnit;
import java.io.*;


function void _WriteToFile(String fn, Object o){
  String vl = o.toString();
  PrintWriter writer = new PrintWriter(fn+".txt", "UTF-8");
  try{
    writer.println(vl);
  } finally {
    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();
  }
  return(result);
}
 
rule "Store values"
timer(int: 1s)                                                                                             
when
  Event($s:source matches "^GV.*", $v:value!="status")
then
  _WriteToFile($s, $v);
end
 
rule "Init values"
  salience 100
when
(OR
  Event($s:source matches "^GV.*", value=="")
  Event($s:source matches "^GV.*", value=="status")
)
then
  execute.command($s, _ReadFromFile($s, "-"));
end


rule "Masterbed music on"
when
Event(source=="SONOS PLAYER STATE", value=="on") AND
Event(source=="GVMASTER_MUSIC_STATUS", value=="on") 
eval(true)
then
execute.command("YAMAHAPOWERON");
execute.command("YAMAHA-MDCDR");
System.out.println("MASTERBED SENSOR ON SONOS IS PLAYING  SET YAMAHA AMP TO MD");
end

Posted by jules_bike at Feb 04, 2015 16:47

it works now, the problem was:
package org.openremote.controller.protocol

before i haD:
package org.openremote.controller.model.event

for some reason these 2 packages can't be combined

this is what i have added now:
//Package, globals and imports:
package org.openremote.controller.protocol
global org.openremote.controller.statuscache.CommandFacade execute;
global org.openremote.controller.statuscache.SwitchFacade switches;
global org.openremote.controller.statuscache.LevelFacade levels;
import org.openremote.controller.protocol.*;
import org.openremote.controller.model.event.*;
import java.lang.Float;
import java.sql.Timestamp;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import org.openremote.controller.utils.Logger;
import org.openremote.controller.Constants;
import java.io.*;

thanks for prompt reply

Posted by robnas at Feb 04, 2015 17:17

one more question.. do you know where the txt file will be saved?

Posted by robnas at Feb 04, 2015 17:28

should be in your /bin folder of openremote

Posted by jules_bike at Feb 04, 2015 18:31

I OpenRemote install from source, MichaƂ Rutka's solution does not work for me, txt files does not save, I haven't a bin directory, I run debian wheezy with tomcat7.

Posted by ludodoucet at Feb 06, 2015 06:36

I think it is at the tomcat7 permissions. an idea? permission java.io.FilePermission

Posted by ludodoucet at Feb 06, 2015 18:18

make sure you have java 6 otherwise drools will not work, if you copy Michal Rutka's command it should work fine a txt file will show up in the openremote/bin dir, for each custom variable 1 file.

only custom sensors can be used, they will be saved

Posted by robnas at Feb 06, 2015 18:25

Suite has a problem of compatibility with fr_FR I compile from source controller .
No files are created in the directory that bin a security problem I think.
I try to find a track.
Maybe a policy file of tomcat7

Posted by ludodoucet at Feb 06, 2015 18:33

If it's can help: catalina.out

java.io.FileNotFoundException: GVMinall.txt (No such file or directory)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:140)
at java.io.FileInputStream.<init>(FileInputStream.java:96)
at org.openremote.controller.protocol.preserve._ReadFromFile._ReadFromFile(_ReadFromFile.java:22)
at org.openremote.controller.protocol.preserve.Rule_Init_values_0.defaultConsequence(Rule_Init_values_0.java:7)
at org.openremote.controller.protocol.preserve.Rule_Init_values_0DefaultConsequenceInvoker.evaluate(Rule_Init_values_0DefaultConsequenceInvoker.java:30)
at org.drools.common.DefaultAgenda.fireActivation(DefaultAgenda.java:917)
at org.drools.common.DefaultAgenda.fireNextItem(DefaultAgenda.java:856)
at org.drools.common.DefaultAgenda.fireAllRules(DefaultAgenda.java:1071)
at org.drools.common.AbstractWorkingMemory.fireAllRules(AbstractWorkingMemory.java:785)
at org.drools.common.AbstractWorkingMemory.fireAllRules(AbstractWorkingMemory.java:751)
at org.drools.impl.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:218)
at org.openremote.controller.statuscache.rules.RuleEngine.push(RuleEngine.java:177)
at org.openremote.controller.statuscache.EventProcessorChain.push(EventProcessorChain.java:188)
at org.openremote.controller.statuscache.StatusCache.update(StatusCache.java:260)
at org.openremote.controller.model.sensor.Sensor.update(Sensor.java:342)
at org.openremote.controller.model.sensor.Sensor$DeviceReader.run(Sensor.java:608)
at java.lang.Thread.run(Thread.java:701)

Posted by ludodoucet at Feb 06, 2015 19:44

Yessss it's work,

I replace:

"fn+".txt", "UTF-8"

by:

"/var/lib/tomcat7/webapps/controller/"+fn+".txt", "UTF-8"
Posted by ludodoucet at Feb 07, 2015 09:21
Document generated by Confluence on Jun 05, 2016 09:36