How to parse xml and send telegram message if too high?

  • Platform information:
    • Hardware: OrangePI 3 LTS
    • OS: Armbian
    • openHAB version: 3.4

I have a robomower at our summer cottage by the sea. I have already lost two robomowers to the sea when the water level rose on to the lawn. My idea is to have OpenHAB monitor the sea water level and send me a telegram message when the sea level rises too high.
Sea level measurements can be obtained from the Finnish Meteorlogical Institue API:

http://opendata.fmi.fi/wfs/fin?service=WFS&version=2.0.0&request=GetFeature&storedquery_id=fmi::observations::mareograph::simple&fmisid=134250&starttime=2023-08-09T16:50&endtime=2023-08-09T17:00&parameters=WLEVN2K_PT1S_INSTANT&

Output looks like this:


```yaml
<wfs:FeatureCollection timeStamp="2023-08-09T17:40:55Z" numberReturned="1" numberMatched="1" xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd http://xml.fmi.fi/schema/wfs/2.0 https://xml.fmi.fi/schema/wfs/2.0/fmi_wfs_simplefeature.xsd">
<wfs:member>
<BsWfs:BsWfsElement gml:id="BsWfsElement.1.1.1">
<BsWfs:Location>
<gml:Point gml:id="BsWfsElementP.1.1.1" srsDimension="2" srsName="http://www.opengis.net/def/crs/EPSG/0/4258">
<gml:pos>63.70857 22.68958 </gml:pos>
</gml:Point>
</BsWfs:Location>
<BsWfs:Time>2023-08-09T17:00:00Z</BsWfs:Time>
<BsWfs:ParameterName>WLEVN2K_PT1S_INSTANT</BsWfs:ParameterName>
<BsWfs:ParameterValue>532.0</BsWfs:ParameterValue>
</BsWfs:BsWfsElement>
</wfs:member>
</wfs:FeatureCollection>

I would like write a rule to call the API every hour and retrieve the latest value, extract the sealevel value, in the above example: 532.0, and compare it to 600 and trigger the telegram binding if 600 is exceeded. The FMI binding does not provide sea level data, so this probably has to be coded by hand, and is beyond my abilities. Once I get the trigger sorted out I do know how to send a telegram message.

This is not really necessary based on the information you’ve provided. This is a textbook case for the http binding:

and the xpath transform

Then you will just have an item with the value of the see level in it and you only need a rule to check the item state to decide about the telegram message.

Thanks! Will check this out. How do I use it to call the latest sea level value?

The http binding allows you to define a thing based on a url (which you have) and will call that url at intervals that you define. There’s good info on the docs page and tons of examples on the forum for using this binding.

Then you get to define custom channels for that thing (in this case a number type channel) with transformation that convert the data returned by the url call into some viable item state. That’s where the xpath transform comes in. All you’ll need to do is work out the xpath call to extract your information of interest. It should be fairly straightforward in this case. If you need to work it out then just search for an xpath testing site and paste in a sample of the returned xml.

Once that’s all set you just link a number item to the channel as in any other case.

You can do all this though the UI so you don’t need to worry about file-based thing configuration if you don’t already do that.

1 Like

As I am more familiar with the linux command line I did a trial with:

curl -s  'http://opendata.fmi.fi/wfs/fin?service=WFS&version=2.0.0&request=GetFeature&storedquery_id=fmi::observations::mareograph::simple&fmisid=134250&starttime=2023-08-09T16:50&endtime=2023-08-09T17:00&parameters=WLEVN2K_PT1S_INSTANT&' | xmlstarlet sel -q -t -v "//BsWfs:ParameterValue" -

which then returns:

532.0

The above line can be put into a shell script and then called via executeCommandLine action.

1 Like

I am a bit more familiar with linux than OpenHAB, but would still need help with how to fetch only the latest value.

what do you mean ? I used the link you provided that file only contains one value as far as I can see.
Can it contain more than one value ?

If you look at the link it contains two parameters: starttime and endtime. Those define the time interval for the measurements. If you omit those two like this:

http://opendata.fmi.fi/wfs/fin?service=WFS&version=2.0.0&request=GetFeature&storedquery_id=fmi::observations::mareograph::simple&fmisid=134250&parameters=WLEVN2K_PT1S_INSTANT&

it provides something like 40 last measurements. I would only like to read the last one.

add

| tail -1

at the end of the command to just show the last value.

curl -s  'http://opendata.fmi.fi/wfs/fin?service=WFS&version=2.0.0&request=GetFeature&storedquery_id=fmi::observations::mareograph::simple&fmisid=134250&parameters=WLEVN2K_PT1S_INSTANT&' | xmlstarlet sel -q -t -v "//BsWfs:ParameterValue" - | tail -1
1 Like

Works!

1 Like

It works very well, indeed! Thanks for the help!

1 Like

I tried this, but as this is the first time I attempted using the xpath transformation I was not able to figure out the correct path. Will try this later, as this is an interesting route to solve the challenge, too.

As you’ve already seen from Wolfgang_S’s answer, //BsWfs:ParameterValue is the xpath that gets you all of the results. In xpath you can also directly filter using last() and then extract only the text from the selected element. Probably something like this should work:

(//BsWfs:ParameterValue)[last()]/text()

Tried the http binding method, but got stuck:

There are a couple of things you can always do to troubleshoot the html binding.

  1. Check you logs and make sure the binding isn’t throwing any obvious errors.

  2. Create a basic channel for a string item with no transform on it. Make sure that the string item gets populated by the page content that you expect. If not then you know the problem is the configuration of the binding.

  3. If you can show that the binding is collecting the full returned content into a string item then there’s something wrong with the transform and you can test out the xpath statement with any online xpath tool such as https://www.freeformatter.com/xpath-tester.html

1 Like

This i what is logged:

</wfs:FeatureCollection>
' with function '(XPATH://BsWfs:ParameterValue)[last()]/text()' and format '%s'
2023-08-28 14:56:16.338 [WARN ] [org.eclipse.jetty.http.HttpParser   ] - URI is too large >8192
2023-08-28 14:56:16.353 [WARN ] [org.eclipse.jetty.http.HttpParser   ] - URI is too large >8192
2023-08-28 14:56:46.200 [WARN ] [.profiles.XPathTransformationProfile] - Could not transform state '<?xml version="1.0" encoding="UTF-8"?>
<wfs:FeatureCollection

This works. The item gets populated with the XML file I am looking for.
How to apply the XPATH is still a mystery…

This is what the xpath tool above shows as output:

Text='119.0'

Execept for the “Text=” this is what I was looking for.

Where should the XPATH be applied? In the channel or item? If so, how?

If Telegram has a place to define a transformation on the Channel, do it there. If not, apply the XPath transform on a Profile on the Link between the Channel and the Item. Or create a Rule and use the transform action.

With the Http binding, that depends entirely on your use case. If you think you are only ever gong to need this one bit of information then, you can apply it to the channel. When you define the channel with the UI, there’s a place right in the wizard to apply the transform:

So, you would make it a number channel and add the XPath transform definition to the channel.

On the other hand if you think that you might want to extra other information someday form this same xml then you should leave the channel as a String Channel, and link a Number Item to it using a profile to apply the transformation:

I have situations where I do both of these things.

That looks correct to me. The text= is just output formatting of the xpath tester. If you unselect the item type option on the page, you will see that the actual return value is just the number:

Check your transform definition, I think the problem is probably the position of that first ( which, if you’re putting it in the channel transform slot should be after the XPATH:

XPATH:(//BsWfs:ParameterValue)[last()]/text()

The HTTP binding can populate multiple channels from one message/document. So even in this case one can define a separate Channel for each piece of data and use a different transform for each to extract all that is desired from the one XML document.

True. I should have been more clear. I just prefer to do it with one raw channel and item profiles in situations like this because that structure is more intuitive to me, personally.