HowTo: Integrate NextPVR recording information

At first I have to say that I’ve only played around OpenHAB for a few weeks so there’s a good chance that everything mentioned below can be done in a more efficient way. That being said, everything demonstrated below works. I also have to thank @psyciknz for getting me started and sub, the original creator of NextPVR.
This howto covers a range of topics from creating items and rules to xslt-transformations and finally (a crude) template widget for HABPanel.
My goal was to get info regarding upcoming recordings into my dashpanel. As I progressed I thought it would be nice to also get tuner status and disk space info, which is all included. As stated above I consider myself a newbie, so I can’t explain how everything works (specially regarding the DateTime-part), but it does. If someone more knowledgeable wants to explain, please feel free to do so. Now to the interesting part!

The items associated with the information are:

String RecordingTitle1 "Next recording title" {http="<[nextPVRcache:60000:XSLT(nextPVRtitle1.xsl)]"}
String RecordingSubtitle1 "Next recording subtitle" {http="<[nextPVRcache:60000:XSLT(nextPVRsubtitle1.xsl)]"}
DateTime RecordingStart1 "Next recording start time" {http="<[nextPVRcache:60000:XSLT(nextPVRstart1.xsl)]"}
DateTime RecordingEnd1 "Next recording end time"
Number RecordingDuration1 "Next recording duration (seconds)" {http="<[nextPVRcache:60000:XSLT(nextPVRduration1.xsl)]"}
String RecordingDescription1 "Next recording description" {http="<[nextPVRcache:60000:XSLT(nextPVRdesc1.xsl)]"}
String RecordingTitle2 "2nd recording title" {http="<[nextPVRcache:60000:XSLT(nextPVRtitle2.xsl)]"}
String RecordingSubtitle2 "2nd recording subtitle" {http="<[nextPVRcache:60000:XSLT(nextPVRsubtitle2.xsl)]"}
DateTime RecordingStart2 "2nd recording start time" {http="<[nextPVRcache:60000:XSLT(nextPVRstart2.xsl)]"}
DateTime RecordingEnd2 "end recording end time"
Number RecordingDuration2 "2nd recording duration (seconds)" {http="<[nextPVRcache:60000:XSLT(nextPVRduration2.xsl)]"}
String RecordingDescription2 "2nd recording description" {http="<[nextPVRcache:60000:XSLT(nextPVRdesc2.xsl)]"}
String RecordingTitle3 "3rd recording title" {http="<[nextPVRcache:60000:XSLT(nextPVRtitle3.xsl)]"}
String RecordingSubtitle3 "3rd recording subtitle" {http="<[nextPVRcache:60000:XSLT(nextPVRsubtitle3.xsl)]"}
DateTime RecordingStart3 "3rd recording start time" {http="<[nextPVRcache:60000:XSLT(nextPVRstart3.xsl)]"}
DateTime RecordingEnd3 "3rd recording end time"
Number RecordingDuration3 "3rd recording duration (seconds)" {http="<[nextPVRcache:60000:XSLT(nextPVRduration3.xsl)]"}
String RecordingDescription3 "3rd recording description" {http="<[nextPVRcache:60000:XSLT(nextPVRdesc3.xsl)]"}
Switch NextPVRStatus "NextPVR recording status"  {http="<[http://ip-of-nextpvr:8866/services/service?method=system.status:600000:XSLT(nextPVRstatus.xsl)]"}
Number NextPVRtotalSpace "Total disk space available to NextPVR" {http="<[http://ip-of-nextpvr.137:8866/services/service?method=system.space:3600000:XSLT(nextPVRtotalsize.xsl)]"}
Number NextPVRfreeSpace "Free disk space available to NextPVR" {http="<[http://ip-of-nextpvr:8866/services/service?method=system.space:30000:XSLT(nextPVRfreesize.xsl)]"}

Nothing really special here The noteworthy items are probably the EndTimes (note that they only have a basic item description and no binding as they are filled in by rules) and use of caching for the http binding. In order to use caching in OH2 you need to edit http.cfg, which you can find under conf/services. You need to have the following lines:

# configuration of the first cache item
http:nextPVRcache.url=http://ip_of_nextpvr:8866/services?method=recording.list&filter=Pending&sid=ohab
http:nextPVRcache.updateInterval=60000

Recording information doesn’t change very ofter, so I chose to cache (and update items) every 60 seconds or so. Now that your basic config is done it’s time to read in the information from NextPVR using XSLT transformations. I won’t list them all here as they are basically all the same, with just replacement of a word.
So in order to get titles (also applies for subtitle, description and duration, just replace the XML tag) you need the following transformation:

<?xml version="1.0"?>
<xsl:stylesheet 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

        <xsl:output indent="yes" encoding="UTF-8" method="text" omit-xml-declaration="yes" />

    <xsl:template match="/">
			<xsl:value-of select="translate(rsp/recordings/recording[1]/name, '¤¶Ã', 'äö')" />
    </xsl:template>
</xsl:stylesheet>

Some characters come in funny via the http binding (I don’t know if it’s a bug or if my configuration is wrong), hence the translation for ä and ö. For the duration you obviously won’t need it, so your select-line would look like

<xsl:value-of select="rsp/recordings/recording[1]/duration_seconds" />

As for recordings number 2 and 3, replace the [1] with 2 or 3 obviously. The data in nextPVR:s XML is sorted so that the next recording is always first.
Getting the start time of the recording is a bit more interesting, and my xslt looks like this (there are other ways, probably nicer to format the string, but again this works):

<?xml version="1.0"?>
<xsl:stylesheet 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

        <xsl:output indent="yes" encoding="UTF-8" method="text" omit-xml-declaration="yes" />

    <xsl:template match="/">
            <xsl:variable name="in" select="rsp/recordings/recording[1]/start_time"/>
            <xsl:variable name="datepart" select="substring-before($in, ' ')"/>
            <xsl:variable name="timepart" select="substring-after($in, ' ')"/>
            <xsl:variable name="year" select="substring-after(substring-after($datepart, '.'), '.')"/>
            <xsl:variable name="month" select="format-number(substring-before(substring-after($datepart, '.'), '.'), '00')"/>
            <xsl:variable name="day" select="format-number(substring-before($datepart, '.'), '00')"/>
            <xsl:value-of select="concat($year,'-',$month,'-',$day,'T',$timepart)"/>
    		
    </xsl:template>
</xsl:stylesheet>

NextPVR own’t give you the ending time of a recording, but instead its duration so you need a rule to parse the information. This rule looks like this:

import org.joda.time.DateTime
rule "RecordingEnd1"
when
	Item RecordingStart1 received update
then
	val duration = (RecordingDuration1.state as DecimalType).intValue
	val DateTime StartTime = new DateTime((RecordingStart1.state as DateTimeType).calendar.timeInMillis)
	var DateTime EndTime = StartTime.plusSeconds(duration)
	postUpdate(RecordingEnd1, new DateTimeType(EndTime.toCalendar(null)))
end

Obviously the same rule is applied for recordings 2 and 3 too.
Status of the tuners and disk space is imported with the following transformations:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

        <xsl:output indent="yes" encoding="UTF-8" method="text" omit-xml-declaration="yes" />

    <xsl:template match="/">
    <xsl:choose>
        <xsl:when test="//Recording">ON</xsl:when>
        <xsl:when test="//LiveTV">ON</xsl:when>
        <xsl:otherwise>OFF</xsl:otherwise>
    </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

and

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

        <xsl:output indent="yes" encoding="UTF-8" method="text" omit-xml-declaration="yes" />

    <xsl:template match="/">
        <xsl:value-of select="//directory[@name='Default']/free"></xsl:value-of>
    </xsl:template>
</xsl:stylesheet>

Now you have all your items and are basically good to go. to show the info I have the following template in HABPanel:

<div class="row">
  <div class="col-xs-12" style="font-size: 24pt">Seuraavat tallennukset:</div>
</div>
<div class="row text-justify">
  <div class="col-xs-12">
  	<span style="color:#FFCC00; font-size: 24pt">{{itemValue('RecordingTitle1')}}</span><span ng-if="itemValue('RecordingSubtitle1')!=''" style="font-size: 14pt"> {{itemValue('RecordingSubtitle1')}}</span>
    <div><i>{{itemValue('RecordingStart1')| date:"dd.M.yyyy 'kello' H:mm"}} - {{itemValue('RecordingEnd1')| date:"H:mm"}}</i></div>
		<div>{{itemValue('RecordingDescription1')}}</div>
  </div>
</div>
<div class="row text-justify">
  <div class="col-xs-12">
  	<span style="color:#FFCC00; font-size: 24pt">{{itemValue('RecordingTitle2')}}</span><span ng-if="itemValue('RecordingSubtitle2')!=''" style="font-size: 14pt"> {{itemValue('RecordingSubtitle2')}}</span>
    <div><i>{{itemValue('RecordingStart2')| date:"dd.M.yyyy 'kello' H:mm"}} - {{itemValue('RecordingEnd2')| date:"H:mm"}}</i></div>
		<div>{{itemValue('RecordingDescription2')}}</div>
  </div>
</div>
<div class="row text-justify">
  <div class="col-xs-12">
  	<span style="color:#FFCC00; font-size: 24pt">{{itemValue('RecordingTitle3')}}</span><span ng-if="itemValue('RecordingSubtitle3')!=''" style="font-size: 14pt"> {{itemValue('RecordingSubtitle3')}}</span>
    <div><i>{{itemValue('RecordingStart3')| date:"dd.M.yyyy 'kello' H:mm"}} - {{itemValue('RecordingEnd3')| date:"H:mm"}}</i></div>
		<div>{{itemValue('RecordingDescription3')}}</div>
  </div>
</div>
<uib-progressbar class="progress-striped"
max="itemValue('NextPVRtotalSpace')" value="itemValue('NextPVRtotalSpace')-itemValue('NextPVRfreeSpace')" type="primary">
  {{'%d' | sprintf:(1-(itemValue('NextPVRfreeSpace')/itemValue('NextPVRtotalSpace')))*100}} %
</uib-progressbar>
</div>

It doesn’t show the tuner status yet, but it’s just an ON/OFF switch and really trivial to display. Enjoy you view into NextPVR straight from OpenHAB!

Mikael

1 Like