How to show the last X log messages of one certain rule in the UI?

I want to show log messages in the UI, but not all, just the ones that come from a certain rule.
My ugly solution was to create 5 string items and in my rule I have a function that sets the message to the string items:

function LogState(stateMessage)
{
  //create the current DateTime in a nice look for the state string
  var localDateTime = LocalDateTime.now();
  var dateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy - HH:mm:ss");
  var localDateTimeFormatted = localDateTime.format(dateTimeFormatter);  
  var newMessage = localDateTimeFormatted + ' : ' + stateMessage;
  
  //get the old states
  var oldState1 = itemRegistry.getItem('RsAutoModeEast_StateString1').getState();
  var oldState2 = itemRegistry.getItem('RsAutoModeEast_StateString2').getState();
  var oldState3 = itemRegistry.getItem('RsAutoModeEast_StateString3').getState();
  var oldState4 = itemRegistry.getItem('RsAutoModeEast_StateString4').getState();
  //var oldState5 = itemRegistry.getItem('RsAutoModeEast_StateString5').getState();
  
  //move the old states
  events.postUpdate('RsAutoModeEast_StateString2', oldState1);
  events.postUpdate('RsAutoModeEast_StateString3', oldState2);
  events.postUpdate('RsAutoModeEast_StateString4', oldState3);
  events.postUpdate('RsAutoModeEast_StateString5', oldState4);
  
  //set the new state
  events.postUpdate('RsAutoModeEast_StateString1', newMessage);
}

This does work, but it is not very elegant!
I can’t be the first who wants to do something like this, so I was wondering if there is a better way to do it?

Regards
Bernd

Just a high-level idea, not tested if it will work:

Reconfigure log4j to add a ln additional log file that will only have the logs you want

Use exec or http binding to read the log file

While I like the idea of having a separate log file, reading the log and putting the messages into the UI does not make things easier, does it?

With regex transformation it should work in theory.

Which UI? I have a vague idea in the back of my mind that at least one of the widgets supports multiline text. If not, you might be able to do it with the oh-list widget.

If that doesn’t work you are probably out of luck and have to continue to use the multiple string Items.

However, what purpose are these serving? Can Frontail with a filter do the job? Perhaps an Item or Items can represent some sort of state to convey the same information.

Background:
I have light sensors at east, south and west side of my house. I have rules to control my roller shutters.
E.g. if the light sensor on the east goes above a certain threshold, a 5 minute timer gets started. If the light sensor does not fall below the threshold within the 5 minutes, all rollershutters on the east side of the house close 3/4. Something similar for opening. If the light sensor is 15 minutes below the threshold, the rollershutters open.

My wife always complains about the “logic” and I need a quick way to tell her why rollershutters do not open or close, when she thinks they should.
Up to now I only have this in a Layout page, but the information also has to be available on a sitemap; because my wife only uses her iPhone with a sitemap I created…

1 Like

I do something similar with my sprinkler system. But since I am displaying the data in the MainUI, I can store all the data in JSON format in a single string item. This makes the rule fairly compact:

var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.RachioHistory');
var tStamp = time.ZonedDateTime.now();
var timeForm = time.DateTimeFormatter.ISO_OFFSET_DATE_TIME;

historyItem = items.getItem('Sprinkler_TimerBox_History');
historyState = historyItem.state

history = JSON.parse(historyState);
history.unshift({"time": `${tStamp.format(timeForm)}`,"event": `${items.getItem('Sprinkler_TimerBox_ScheduleInfo').state}`});
while (history.length > 10) history.pop();

historyItem.sendCommand(JSON.stringify(history));

And the output is easy to get into a list using a repeater:

Unfortunately, with a sitemap, your choices are very limited. I don’t think a frontail page in a webview element would work because the webviewer loads up a fresh page when viewed and frontail will start with an empty list at that point. You might be able to use a system like the one I show above and then you sitemap elements with the JSONpath transform in their label to extract the relevant information. Then you would only need one item and just setup several different sitemap elements with slightly different JSON paths.

2 Likes

Now this is really elegant!

Even if I cannot use it, I would be interested on how you put the json formatted string into the repeater.
Shame on me, I haven’t used repeaters at all, up to now…

Once you have the item set up properly, the repeater is very straight forward. The item state, as you can see form the rule code is a JSON array (that’s why the unshift and pop methods work) and each element in the array is an object with event and time properties. The JSON string looks like this:

[{"time":"2022-06-10T03:52:40.553-07:00","event":"Basic Watering ran for 137 minutes."},{"time":"2022-06-10T01:35:30.927-07:00","event":"Basic Watering will run for 137 minutes."},{"time":"2022-06-10T00:25:40.361-07:00","event":"Vegetable garden ran for 26 minutes."},{"time":"2022-06-10T00:00:12.883-07:00","event":"Vegetable garden will run for 26 minutes."},{"time":"2022-06-09T02:08:40.282-07:00","event":"Basic Watering ran for 33 minutes."},{"time":"2022-06-09T01:35:32.828-07:00","event":"Basic Watering will run for 33 minutes."},{"time":"2022-06-09T00:25:39.573-07:00","event":"Vegetable garden ran for 26 minutes."},{"time":"2022-06-09T00:00:05.017-07:00","event":"Vegetable garden will run for 26 minutes."},{"time":"2022-06-08T05:06:46.777-07:00","event":"Basic Watering ran for 210 minutes."},{"time":"2022-06-08T01:35:31.52-07:00","event":"Basic Watering will run for 210 minutes."}]

Repeaters are made to handle arrays by default, so all you have to do is parse the state string into JSON and let the repeater use that array. Then every loop in the repeater, the loop variable is the object with the event and time properties so you jsut reference those like you would any other object:

    - component: oh-repeater
      config:
        for: event
        in: =JSON.parse(items.Sprinkler_TimerBox_History.state)
        sourceType: array
      slots:
        default:
          - component: oh-list-item
            config:
              after: =loop.event.event
              title: =dayjs(loop.event.time).fromNow()
2 Likes