XSLT Transformation

Hi !
I have a problem with XSLT, I have following xml file:

<root>
<Device Name="StecaGrid 5003" NominalPower="5000" Type="Inverter" Serial="757508DD006567030026" BusAddress="1" NetBiosName="INV006567030026" IpAddress="192.168.1.71" DateTime="2018-07-20T15:48:48">
<Measurements>
<Measurement Value="224.6" Unit="V" Type="AC_Voltage1"/>
<Measurement Value="227.1" Unit="V" Type="AC_Voltage2"/>
<Measurement Value="224.6" Unit="V" Type="AC_Voltage3"/>
<Measurement Value="0.791" Unit="A" Type="AC_Current1"/>
<Measurement Value="0.806" Unit="A" Type="AC_Current2"/>
<Measurement Value="0.790" Unit="A" Type="AC_Current3"/>
<Measurement Value="529.0" Unit="W" Type="AC_Power"/>
<Measurement Value="177.4" Unit="W" Type="AC_Power1"/>
<Measurement Value="180.3" Unit="W" Type="AC_Power2"/>
<Measurement Value="177.4" Unit="W" Type="AC_Power3"/>
<Measurement Value="50.016" Unit="Hz" Type="AC_Frequency1"/>
<Measurement Value="50.011" Unit="Hz" Type="AC_Frequency2"/>
<Measurement Value="50.015" Unit="Hz" Type="AC_Frequency3"/>
<Measurement Value="420.5" Unit="V" Type="DC_Voltage"/>
<Measurement Value="1.283" Unit="A" Type="DC_Current"/>
<Measurement Value="45.2" Unit="°C" Type="Temp"/>
<Measurement Value="575.5" Unit="V" Type="LINK_Voltage"/>
<Measurement Unit="W" Type="GridPower"/>
<Measurement Unit="W" Type="GridConsumedPower"/>
<Measurement Unit="W" Type="GridInjectedPower"/>
<Measurement Unit="W" Type="OwnConsumedPower"/>
<Measurement Value="100.0" Unit="%" Type="Derating"/>
</Measurements>
</Device>
</root>

I want get AC_Power:
<Measurement Value="529.0" Unit="W" Type="AC_Power"/>

so value “529.0” where type is AC_Power.
Does anybody know how to do this ?

BTW. Is it any method to distinguish if webserver build-in my inverter has other xml pages ?
I need to get more that then in my xml.

There’s your problem! :stuck_out_tongue:

I joke but XSL is a real pain to deal with.

Why not just use XPATH?

XPATH(/root/Device/Measurements/Measurement[@Type="AC_Power"]/@Value)

You will have to use the XPATH inside your XSL anyway so just skip needing to write a separate .xsl file.

Find the documentation for the API.

Thanks a lot !

Anyway I had a problem with proper implementation, I have tried to use it with xsl:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">       
   <xsl:output indent="yes" method="xml" encoding="UTF-8" omit-xml-declaration="yes" />
   <xsl:template match="/">
      <xsl:for-each select="root/Device/Measurements">
	<xsl:value-of select="Measurement[@Type="AC_Power"]/@Value" />
   </xsl:template>

</xsl:stylesheet>

or just:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">       
   <xsl:output indent="yes" method="xml" encoding="UTF-8" omit-xml-declaration="yes" />
   <xsl:template match="/">
      <xsl:value-of select="//root/Device/Measurements/Measurement[@Type="AC_Power"]/@Value"/>
   </xsl:template>
</xsl:stylesheet>

and get:

2018-08-01 13:18:30.940 [ERROR] [.i.s.XsltTransformationService] - transformation throws exception
javax.xml.transform.TransformerConfigurationException: Element type "xsl:value-of" must be followed by either attribute specifications, ">" or "/>".

What do you mean by that ? If I want to extract few values I need to have xsl file for each item/value, am I right ?

No, you just need the XPATH transform and put the xpath string that you are using already in the value-of element to select the value.

When I put it in xsl file and call transformation from an item I got error, as I showed in previous post.

Tring to find similar example but most of the xml looks diff. OH wiki also tell nothing about Xpath:

The error is some problem with the XSL syntax. I avoid XSL at almost all costs so I can’t really help solve that error. And since you are not actually transforming the XML into some other XML or into some other format and just pulling the value of an attribute, XSL is the wrong tool for the task. You should be using XPATH transform for that.

If you are running OH 2.x you should be using https://www.openhab.org/docs/ as the source of the documentation. If you are using OH 1.8 and using any binding newer than 1.8 you should be using https://www.openhab.org/addons/ for your documentation. The OH 1 wiki has not been maintained for almost two years now.

I provided a link above to the Xpath documentation.

Yes error was XSL Syntax. Then I recognized that I can use XPATH transformation directly, so it looks working:

Number Energy_Prod "Energia produkowana [%.0f W]" <energy> (Energy) {http="<[http://192.168.1.71/measurements.xml:10000:XPATH(/root/Device/Measurements/Measurement[@Type='AC_Power']/@Value)]"}

Before I had a problem as well with syntax, because “AC_Power” was interpreted as end of http link, but I have changed " to ’ and it works :slight_smile:
Thanks for that.

BTW. Here in item I can provide refresh time in ms, but it is for PV inventer which works only during a day, so there is no sense to refresh it every seconds or minute. Can I somehow write a rule which will triger refresh every minute during day only (I can use time crone and assume fix daylights hours) ?

Not with the HTTP binding unfortunately. Personally, hitting the URL every 10 seconds or a minute at night is a problem not worth solving. But if you want to try to solve that, you will have to switch to the sendHttpGetRequest Actions and a cron triggered Rule. Use the Quartz cron builder (search google) to construct the expression; that’s the best tool I’ve used for this purpose.

In the Rule you will have:

rule "Get Power"
when
    // cron expression
then
    val results = sendHttpGetRequest("http://192.168.1.71/measurements.xml")
    Energy_Prod.postUpdate(transform("XPATH", "/root/Device/Measurements/Measurement[@Type='AC_Power']/@Value", results)
end

If you want to get really clever, you can use Astro to calculate sunrise and sunset and only update between those times.

rule "Get Power"
when
    Time cron "0 * * * ? *" // this expression is probably wrong, should be every minute
then
    if((Sunrise.state as DataTimeType).isAfter(now.millis) || (Sunset.state as DateTimeType).isBefore(now.millis)) return;
    val results = sendHttpGetRequest("http://192.168.1.71/measurements.xml")
    Energy_Prod.postUpdate(transform("XPATH", "/root/Device/Measurements/Measurement[@Type='AC_Power']/@Value", results)
end

The added if statement will exit the Rule if the current time is before sunrise or after sunset.