How to read a File Manager file using a constructed string for the file name?

The documentation for Hubitat says this (edited for brevity):

"Rule Machine offers actions to directly write, append, or delete File Manager files. ... Rule Machine also offers the ability to read File Manager files. One way is by setting a local (or hub) variable of type String to the contents of the file [by] selecting Set from local file under Select string operation. Alternatively, file contents can be read or set in rules anywhere that %variable-name% can be used by using the format %/local/file-name% instead, where file-name is the name of an existing file. ..."

The first method requires selection of pre-existing names from a drop-down list.

The second method requires hard-coding the file name into to the %/local/file-name% format.

I have a variable vVar that contains a file name string "TestFileNameString". I want to read a corresponding file from File Manager using the value of the vVar variable, rather than hard coding the name. Is that possible?

BTW, I constructed a string variable, vTestReadString2, with the content of:
%/local/TestFileNameString%

However, this command failed to read the named file:
Set vTestReadString2 to '%vFileNameString2%(%/local/TestFileNameString%)'

Instead, vTestReadString2 is set to %/local/TestFileNameString%

Try it without the percents, i.e. /local/TestFileNameString

That doesn't seem to work. My actual test Rule is shown below. Note:

  • The file is successfully written with the name OfficeDownlightsDimmerIB.
  • Method 1 (using a drop down menu to pick the file name) successfully reads the file.
  • Method 2 fails.
  • The disabled line was used to construct my original test string of %/local/OfficeDownlightsDimmerIB% since I didn't know how to escape the % symbol. The previous line originally said Set vFileNameString2 to '#/local/%vFileNameString%%' (the # was replaced by % by the disabled line).
  • The version of the rule below drops the extra percents in the Set vFileNameString2 line.

I'm just curious why you are using files at all for this. It looks like you are just saving a fairly short string to a file, so why not use a String hub variable? Maybe I am missing something.

I have a rule that I apply to 30+ in Inovelli Blue dimmers. I used to use hub variables and an ON and an OFF rule for each dimmer. Since I tinker with the rule a lot, every time I made a change to my ā€œmasterā€œ rule, Iā€™d have to propagate over 60 rules out to the other dimmers.

The string you see sets the values for all of my internal variables - 4 modes of operation, dimming thresholds, high, low, & medium dimming values, and start and stop times for a night time period.

My new approach is to use a single rule per dimmer that can distinguish between different button presses and act accordingly. This cuts the number of rules I need down by half. One aspect of my new approach is that each rule can read a common limited string and set all of its initial variable values. This means that I only need to change one variable string per rule to customize a rule for its specific dimmer.

I want to take this a step further. Rather than have to edit the long string in each cloned rule, Iā€™m intending to write an initialization rule that creates files with a string for each dimmer, and let each dimmer use its device name to read back the corresponding file and set all of the parameter values automatically. That will substantially cut down the amount of time I need to spend on customizing each dimmer rule after cloning.

1 Like

Have you looked at File Manager Device? It is a driver that creates a Device to manage files. It has a file read command. You can set in preferences to read a file to an attribute. Then from a rule you call readFile('filename'), and then pull the created attribute fileContent to get the file results into a string.

This would be the webcore equivalent rule to read the file contents:

fileManager

Too bad you cannot use maps in Rule Machine, I store all my device data for each scene in a single map using a custom app for my scene control, which makes it easy to iterate through the data and pull the values out of the map to set devices.

Edit: I'm surprised @thebearmay didn't suggest this, it is his driver

If you store the data in a JSON string you can parse it pretty much the same way as you would for a HashMap.

Yup, I used JSON strings for scenes for a few years, originally on Webcore. When I moved everything into custom apps I was still using JSON strings until just a few months ago when I switched over to using maps, like this one below for my living room scenes using light level variables set from my indoor light levels app. I probably overdo it on my number of scenes I use :smiley:

[
"Dawn":[
0:[d:"Bottle Lamp",s:"off",l:"NA"],
1:[d:"Ceramic Lamp",s:"on",l:"10"],
2:[d:"Entry Stairs",s:"off",l:"NA"],
3:[d:"Living Room Ceiling",s:"on",l:"20"],
4:[d:"Standing Lamp",s:"on",l:"lLL"],
5:[d:"Table Lamp",s:"on",l:"lLL"],
6:[d:"Tree Lamp",s:"off",l:"NA"]
],
"Morning":[
0:[d:"Bottle Lamp",s:"off",l:"NA"],
1:[d:"Ceramic Lamp",s:"on",l:"25"],
2:[d:"Entry Stairs",s:"off",l:"NA"],
3:[d:"Living Room Ceiling",s:"on",l:"20"],
4:[d:"Standing Lamp",s:"on",l:"lLL"],
5:[d:"Table Lamp",s:"on",l:"lLL"],
6:[d:"Tree Lamp",s:"on",l:"50"]
],
"Day":[
0:[d:"Bottle Lamp",s:"off",l:"NA"],
1:[d:"Ceramic Lamp",s:"on",l:"mLL"],
2:[d:"Entry Stairs",s:"off",l:"NA"],
3:[d:"Living Room Ceiling",s:"on",l:"lLL-10"],
4:[d:"Standing Lamp",s:"on",l:"mLL"],
5:[d:"Table Lamp",s:"on",l:"mLL"],
6:[d:"Tree Lamp",s:"on",l:"50"]
],
"Late Evening":[
0:[d:"Bottle Lamp",s:"on",l:"mLL"],
1:[d:"Ceramic Lamp",s:"on",l:"75"],
2:[d:"Entry Stairs",s:"off",l:"NA"],
3:[d:"Living Room Ceiling",s:"on",l:"20"],
4:[d:"Standing Lamp",s:"on",l:"mLL"],
5:[d:"Table Lamp",s:"on",l:"mLL"],
6:[d:"Tree Lamp",s:"off",l:"NA"]
],
"Dinner":[
0:[d:"Bottle Lamp",s:"on",l:"mLL"],
1:[d:"Ceramic Lamp",s:"on",l:"90"],
2:[d:"Entry Stairs",s:"on",l:"15"],
3:[d:"Living Room Ceiling",s:"on",l:"20"],
4:[d:"Standing Lamp",s:"on",l:"65"],
5:[d:"Table Lamp",s:"on",l:"65"],
6:[d:"Tree Lamp",s:"on",l:"lLL"]
],
"Evening":[
0:[d:"Bottle Lamp",s:"on",l:"lLL"],
1:[d:"Ceramic Lamp",s:"on",l:"50"],
2:[d:"Entry Stairs",s:"off",l:"NA"],
3:[d:"Living Room Ceiling",s:"on",l:"20"],
4:[d:"Standing Lamp",s:"on",l:"mLL"],
5:[d:"Table Lamp",s:"on",l:"lLL"],
6:[d:"Tree Lamp",s:"on",l:"lLL"]
],
"TV Time":[
0:[d:"Bottle Lamp",s:"on",l:"25"],
1:[d:"Ceramic Lamp",s:"on",l:"25"],
2:[d:"Entry Stairs",s:"off",l:"NA"],
3:[d:"Living Room Ceiling",s:"on",l:"25"],
4:[d:"Standing Lamp",s:"on",l:"lLL"],
5:[d:"Table Lamp",s:"on",l:"lLL"],
6:[d:"Tree Lamp",s:"on",l:"25"]
],
"Dim":[
0:[d:"Bottle Lamp",s:"off",l:"NA"],
1:[d:"Ceramic Lamp",s:"on",l:"5"],
2:[d:"Entry Stairs",s:"off",l:"NA"],
3:[d:"Living Room Ceiling",s:"on",l:"20"],
4:[d:"Standing Lamp",s:"on",l:"lLL"],
5:[d:"Table Lamp",s:"on",l:"lLL"],
6:[d:"Tree Lamp",s:"off",l:"NA"]
],
"Bright":[
0:[d:"Bottle Lamp",s:"on",l:"mLL"],
1:[d:"Ceramic Lamp",s:"on",l:"100"],
2:[d:"Entry Stairs",s:"on",l:"20"],
3:[d:"Living Room Ceiling",s:"on",l:"hLL"],
4:[d:"Standing Lamp",s:"on",l:"hLL"],
5:[d:"Table Lamp",s:"on",l:"hLL"],
6:[d:"Tree Lamp",s:"on",l:"mLL"]
],
"Lamps Off":[
0:[d:"Bottle Lamp",s:"off",l:"NA"],
1:[d:"Ceramic Lamp",s:"off",l:"0"],
2:[d:"Entry Stairs",s:"off",l:"NA"],
3:[d:"Living Room Ceiling",s:"on",l:"20"],
4:[d:"Standing Lamp",s:"off",l:"NA"],
5:[d:"Table Lamp",s:"off",l:"NA"],
6:[d:"Tree Lamp",s:"off",l:"NA"]
],
"All Off":[
0:[d:"Bottle Lamp",s:"off",l:"NA"],
1:[d:"Ceramic Lamp",s:"off",l:"0"],
2:[d:"Entry Stairs",s:"off",l:"NA"],
3:[d:"Living Room Ceiling",s:"off",l:"NA"],
4:[d:"Standing Lamp",s:"off",l:"NA"],
5:[d:"Table Lamp",s:"off",l:"NA"],
6:[d:"Tree Lamp",s:"off",l:"0"]
],
"Night":[
0:[d:"Bottle Lamp",s:"off",l:"NA"],
1:[d:"Ceramic Lamp",s:"off",l:"NA"],
2:[d:"Entry Stairs",s:"off",l:"NA"],
3:[d:"Living Room Ceiling",s:"off",l:"NA"],
4:[d:"Standing Lamp",s:"off",l:"NA"],
5:[d:"Table Lamp",s:"off",l:"NA"],
6:[d:"Tree Lamp",s:"off",l:"NA"]
]
]

    Map sceneMap = getSceneMap(room)                                    // get the scenes map for room
    def devices = sceneMap."${scene}".collect{entry -> entry.value}     // get a map of device commands for the current room scene from the sceneMap

    devices.each{device -> 
        if (!(device.s == "NA" && device.l == "NA")) {
            def thisDevice = getRoomDevice(room, device.d)
            commandDevice(thisDevice, device.s, device.l, room)  // device, switch, level, room
            pauseExecution(exDelay)
        }
    }

Thank you! That worked a treat! My scheme is further along -- One Rule to bind them...

So below is a snippet of my Inovelli Blue dimmer rule that allows the rule (if the down paddle is tapped 5 times) to read its own local variable values from a file (the parsing of the read string in vInitialSettings and the setting of those values is further on in the rules, omitted for brevity).

The %device% variable is stripped of spaces by a Rule Function (posted at Rule Machine: Rule Functions - #19 by John_Land) to form a valid file name. Then my initials are pre-pended so that all of these types of files are grouped together. If a file does not exist, then the vInitialSettings variable will default to values set in the Local Variables table.