Use XMLTV with openHAB

Maybe theres someone else, who is interested in working with EPG-informations in his or her openHAB environment.

This soloution is based on my wifes wish, having a simple ad-free way to see the current and next-running tv-show on all the commonly used channels in our country (and switching on the TV with the current channel running / or setting a timer for recording shows in the background / etc.).

This is just a first simple method to implement the most relevant infos into openHAB with the help of xmltv (http://wiki.xmltv.org/index.php/Main_Page) which serves as a base for e.g. a kodi-/tv-binding implementation (which is not a part of this implementation, yet).

But first things first - a word of caution:

  1. I´m not fully aware of the current legal status for grabbing channel- and show-informations from multiple web-sources (this is what a xmltv-grabber do) - so please consider checking your countries law, before grabbing data with xmltv.
  2. I´m not a developer, nor a skilled scripter (is there even a difference?! - whatever) - so please use any of the provided infos/scrips with caution and feel free to review anything - I´m willed to learn and making things better in the future.
  3. I´m using openHABian v1.4.1 on a Rpi3b+ for myself and the shown install-script and OH-rule may not work with a different directory-structure than the default (’/etc/openhab2/’) - but it should be realy easy to rewrite the script to your own needs.

So - what can this one do for you?

  • Showing Title and Subtitle for the currently running and next tv-show for all your selected channels
  • Start / Stoptime for currently running and next tv-show
  • There are a lot more informations available (like show description, cast-infos, channel-logos etc.) - but depending on the used xmltv-source, these informations may be incomplete - so I skipped them in this first version.


(EPG.sitemap - channel overview)


(EPG.sitemap - current show details)


(EPG.sitemap - next show details)

Installation steps

  1. Install & configure xmltv
    1.1. Automatic: Using my rudimentary bash install-script from here
    1.2. Manual: Follow these steps:
# Update system and install XMLTV
sudo apt-get update
sudo apt-get install xmltv

# Get 'epg_parser.py' from github - to extract values from the xml-files
sudo -u openhab wget https://raw.githubusercontent.com/sparetimetherapist/xlmtv_for_openhab/master/epg_parser.py -O /etc/openhab2/scripts/epg_parser.py

# Creating the needed directories
sudo -u openhab mkdir -p /etc/openhab2/html/epg/{data/tmp,conf}

# Configuring xmltv-grabber (this one is for EU channels)
sudo -u openhab /usr/bin/tv_grab_eu_egon --configure --config-file /etc/openhab2/html/epg/conf/epg_eu.conf

# Only execute the following if your timezone is not UTC (your local timezone is CET in this example)
sudo sed -i -e 's/UTC/CET/g' /usr/bin/tv_grep

# Now edit your xmltv-settings, using your favourite editor (exchange '!' with '=' to add a channel)
sudo -u openhab nano /etc/openhab2/html/epg/conf/epg_eu.conf
  1. Install EXEC-Binding and MAP / JS Transformation in openHAB
    2.1. epg_duration.js

  2. Create stuff in OH
    3.1. Reffer XMLTV-channelIDs to OH by creating a epg.map in the following syntax (depending on the channels you choose in the ‘epg_eu.conf’)

NULL=none
-=none
1=hd.daserste.de
2=hd.zdf.de

3.2. Create EPG.items (example for 2 channels / Create a new block for each channel you want to add)

Group       gTVChannel
Group       gTVStopTime

Number      EPG_Update                          "EPG Status [%s]"

///////////////////////// Channel 1 /////////////////////////////
String      TV_Channel_1_Name                   "Sender [%s]"                                                           (gTVChannel)
String      TV_Channel_1_CurrentShow            "Aktuell läuft [%s]"                                <live>
String      TV_Channel_1_NextShow               "Danach läuft [%s]"
DateTime    TV_Channel_1_CurrentShowStartTime   "Startzeit [%1$td.%1$tm.%1$tY - %1$tH:%1$tM Uhr]"
DateTime    TV_Channel_1_NextShowStartTime      "Startzeit [%1$td.%1$tm.%1$tY - %1$tH:%1$tM Uhr]"
DateTime    TV_Channel_1_CurrentShowStopTime    "Stopzeit [%1$td.%1$tm.%1$tY - %1$tH:%1$tM Uhr]"                        (gTVStopTime)
DateTime    TV_Channel_1_NextShowStopTime       "Stopzeit [%1$td.%1$tm.%1$tY - %1$tH:%1$tM Uhr]"
String      TV_Channel_1_NextShowStartStop      "Start / Ende [%s Uhr]"
String      TV_Channel_1_CurrentShowDuration    "Verbleibende Zeit [JS(epg_duration.js):%s]"
/////////////////////////////////////////////////////////////////

///////////////////////// Channel 2 /////////////////////////////
String      TV_Channel_2_Name                   "Sender [%s]"                                                           (gTVChannel)
String      TV_Channel_2_CurrentShow            "Aktuell läuft [%s]"                                <live>
String      TV_Channel_2_NextShow               "Danach läuft [%s]"
DateTime    TV_Channel_2_CurrentShowStartTime   "Startzeit [%1$td.%1$tm.%1$tY - %1$tH:%1$tM Uhr]"
DateTime    TV_Channel_2_NextShowStartTime      "Startzeit [%1$td.%1$tm.%1$tY - %1$tH:%1$tM Uhr]"   
DateTime    TV_Channel_2_CurrentShowStopTime    "Stopzeit [%1$td.%1$tm.%1$tY - %1$tH:%1$tM Uhr]"                        (gTVStopTime)
DateTime    TV_Channel_2_NextShowStopTime       "Stopzeit [%1$td.%1$tm.%1$tY - %1$tH:%1$tM Uhr]"
String      TV_Channel_2_CurrentShowStartStop   "Start / Ende [%s Uhr]"
String      TV_Channel_2_NextShowStartStop      "Start / Ende [%s Uhr]"
String      TV_Channel_2_CurrentShowDuration    "Verbleibende Zeit [JS(epg_duration.js):%s]"
/////////////////////////////////////////////////////////////////

3.3. Create your OH rule-file: EPG.rule (works with as many channels as you want)

import java.io.File // You need to import this one for file-checking

// #############################################################
//                      Global EPG Settings
// #############################################################
//
// ----------------------- PREREQUISITES -----------------------
// 'Exec' Binding
// 'MAP' Transformation (including 'epg.map')
// 'xmltv' installed ('sudo apt-get install xmltv')
// 'epg_parser.py' in OH script-directory
// 
// 'JS' Transformation (for easier duration-calculation) [OPTIONAL]
// -------------------------------------------------------------
//
val xml_grabber = "tv_grab_eu_egon" // There may be a need to change the used grabber, depending on your region (this one is for EU channels)
val xml_path = "/etc/openhab2/html/epg/"
val script_path = "/etc/openhab2/scripts/"
//
// #############################################################

// -------------------------------------------------------------> START
//
//                      EPG Rule #1
//
// This rule crawls all the public available EPG-Sources based
// on the selection in your xmltv-config.
//
// This one requires internet-connection to execute!
//
// -------------------------------------------------------------
rule "EPG - Update 2-day forecast XMLTV-file"
when
    System started or // Execute on system startup...
    Time cron "0 3 0 * * ?" or // ... every night at 3 o'clock...
    Item EPG_Update received update 0 // ... and if another rule sends update '0' to 'EPG_Update'.
then
    // Define variables
    val epgConfig = new File(xml_path + "conf/epg_eu.conf")
    val epgSource = new File(xml_path + "data/epgsource.xml")

    // Abort if xmltv-config is missing
    if (epgConfig.isFile() == false || epgConfig.canRead() == false) {
        logError("EPG","EPG Error: Update/Initialization failed ('epg_eu.conf' is missing or can´t be accessed)")
        return
    }

    // Check if epgsource.xml is available...
    if (epgSource.isFile() && epgSource.canRead()) {
        val newdate = new DateTime(now().minusHours(12))
        val filedate = new DateTime(epgSource.lastModified())

        // Refresh epgsource.xml if it´s lastModified date is older>12h
        if(filedate.isBefore(newdate)) {
            logInfo("EPG","EPG: 'epgsource.xml' is available, but older than 12 hours - Starting update... ")
            executeCommandLine("sudo /usr/bin/" + xml_grabber + " --config-file " + xml_path + "conf/epg_eu.conf --days 2 --quiet --output " + xml_path + "data/epgsource.xml",5000)
            sendCommand(EPG_Update, 1) // continue with rule step #2
            return
        } else { return }
    } else {
        // Execute if epgsource.xml can´t be found or accessed.
        logInfo("EPG","EPG: 'epgsource.xml ' isn´t available or can´t be accessed - Fetching EPG-data...")
        executeCommandLine("sudo /usr/bin/" + xml_grabber + " --config-file " + xml_path + "conf/epg_eu.conf --days 2 --quiet --output " + xml_path + "data/epgsource.xml",5000)
        sendCommand(EPG_Update, 1) // continue with rule step #2
    }
end // -------------------------------------------------------------> END

// -------------------------------------------------------------> START
//
//                          EPG Rule #2
//
// This one minimizes the 'epgsource.xml' hourly, by creating
// another file for all shows running until NOW.
// 
// -------------------------------------------------------------
rule "EPG - Minimize XMLTV-file every hour"
when
    Time cron "0 0 * * * ?" or // Execute every hour...
    Item EPG_Update received update 1 // ... and if rule #1 sends a trigger
then
    // Check for 'epgsource.xml'
    val epgSource = new File(xml_path + "data/epgsource.xml") 
    if(epgSource.isFile() && epgSource.canRead()) { 

        // Extracts all shows running until NOW, depending on your local time
        executeCommandLine("sudo /usr/bin/tv_grep --on-after now --output " + xml_path + "data/epgsource_bynow.xml " + xml_path + "data/epgsource.xml",5000)
        sendCommand(EPG_Update, 2) // continue with rule #3 
        logInfo("EPG","EPG: 'epgsource.xml' successfully minimized.")
    } else {
        sendCommand(EPG_Update, 0) // if 'epgsource' wasn´t found - go back to rule #1...
        return
    }
end // -------------------------------------------------------------> END

// -------------------------------------------------------------> START
//
//                          EPG Rule #3
//
// Extract the current and next tv-shows for all selected
// channels from 'epgsource_bynow.xml'.
//
// -------------------------------------------------------------
rule "EPG - Get current and next tv-show from minimized XMLTV-file"
when
    Item EPG_Update received update 2 // Execute on update '2'
then
    // Check for 'epgsource_bynow.xml'
    val epgFiltered = new File(xml_path + "data/epgsource_bynow.xml")
    if(epgFiltered.isFile() && epgFiltered.canRead()) {

        // Split minimized xmltv-file into one for each channel
        executeCommandLine("sudo /usr/bin/tv_split --output " + xml_path + "data/%channel_bynow.xml " + xml_path + "data/epgsource_bynow.xml",5000) 

        gTVChannel.members.filter[ i | i.state != NULL ].forEach[ i |
            // Extract and transform channelID from item
            val chan = i.name.split("_").get(2).toString
            val channelId = transform("MAP", "epg.map", chan)

            // Query for current- and next-show infos
            val stop = executeCommandLine("sudo python " + script_path + "epg_parser.py -t " + xml_path + "data/" + channelId + "_bynow.xml -c get_stopextra",5000)
            executeCommandLine("sudo /usr/bin/tv_grep --on-after now --on-before now --output " + xml_path + "data/" + channelId + "_now.xml " + xml_path + "data/" + channelId + "_bynow.xml",5000)
            executeCommandLine("sudo /usr/bin/tv_grep --on-after " + stop + " --on-before " + stop + " --output " + xml_path + "data/" + channelId + "_next.xml " + xml_path + "data/" + channelId + "_bynow.xml",5000)
        ]

        // Continue to rule #4
        sendCommand(EPG_Update, 3) 
        return

    } else {
        logError("EPG", "EPG Error: Update failed ('epgsource_bynow.xml' is missing or can´t be accessed)" )
        sendCommand(EPG_Update, 1) // return to rule #2
        return
    }
end // -------------------------------------------------------------> END

// -------------------------------------------------------------> START
// 
//                          EPG Rule #4
//
// Extract all values from the *.xml-files and send them to 
// the corresponding openHAB-items.
//
// -------------------------------------------------------------
rule "EPG - Send updated infos to openHAB-items"
when
    Item EPG_Update received update 3 // Execute on update #3
then
    gTVChannel.members.forEach[ i |
        val room = i.name.split("_").get(0).toString
        val type = i.name.split("_").get(1).toString
        val chan = i.name.split("_").get(2).toString
        
        // Set variable item-names & transform values
        val currentShowTitleItem = room + "_" + type + "_" + chan + "_CurrentShow"
        val nextShowTitleItem = room + "_" + type + "_" + chan + "_NextShow"
        val currentShowStartTimeItem = room + "_" + type + "_" + chan + "_CurrentShowStartTime"
        val nextShowStartTimeItem = room + "_" + type + "_" + chan + "_NextShowStartTime"
        val currentShowStopTimeItem = room + "_" + type + "_" + chan + "_CurrentShowStopTime"
        val nextShowStopTimeItem = room + "_" + type + "_" + chan + "_NextShowStopTime"
        // val currentShowStartStopItem = room + "_" + type + "_" + chan + "_CurrentShowStartStop"
        val nextShowStartStopItem = room + "_" + type + "_" + chan + "_NextShowStartStop"
        // val logoURLItem = room + "_" + type + "_" + chan + "_LogoURL"
        val channelId = transform("MAP", "epg.map", chan)

        // Execute python-script
        val channelName = executeCommandLine("sudo python " + script_path + "epg_parser.py -t " + xml_path + "data/" + channelId + "_now.xml -c get_channelname",5000)
        val currentShowTitle = executeCommandLine("sudo python " + script_path + "epg_parser.py -t " + xml_path + "data/" + channelId + "_now.xml -c get_title",5000)
        val nextShowTitle = executeCommandLine("sudo python " + script_path + "epg_parser.py -t " + xml_path + "data/" + channelId + "_next.xml -c get_title",5000)
        val currentShowStartTime = executeCommandLine("sudo python " + script_path + "epg_parser.py -t " + xml_path + "data/" + channelId + "_now.xml -c get_starttime",5000)
        val nextShowStartTime = executeCommandLine("sudo python " + script_path + "epg_parser.py -t " + xml_path + "data/" + channelId + "_next.xml -c get_starttime",5000)
        val currentShowStopTime = executeCommandLine("sudo python " + script_path + "epg_parser.py -t " + xml_path + "data/" + channelId + "_now.xml -c get_stoptime",5000)
        val nextShowStopTime = executeCommandLine("sudo python " + script_path + "epg_parser.py -t " + xml_path + "data/" + channelId + "_next.xml -c get_stoptime",5000)
        // val currentShowStartStop = executeCommandLine("sudo python " + script_path + "epg_parser.py -t " + xml_path + "data/" + channelId + "_now.xml -c get_startstop",5000)
        val nextShowStartStop = executeCommandLine("sudo python " + script_path + "epg_parser.py -t " + xml_path + "data/" + channelId + "_next.xml -c get_startstop",5000)
        // val logoURL = executeCommandLine("sudo python " + script_path + "epg_parser.py -t " + xml_path + "data/" + channelId + "_now.xml -c get_logourl",5000)

        // Sending stuff to items
        sendCommand(i.name, channelName)
        sendCommand(currentShowTitleItem, currentShowTitle)
        sendCommand(nextShowTitleItem, nextShowTitle)
        if (currentShowStartTime != "" || currentShowStartTime != NULL) { sendCommand(currentShowStartTimeItem, currentShowStartTime.toString) }
        sendCommand(nextShowStartTimeItem, nextShowStartTime.toString)
        if (currentShowStopTime.toString != "" || currentShowStopTime.toString != NULL) { sendCommand(currentShowStopTimeItem, currentShowStopTime.toString) }
        sendCommand(nextShowStopTimeItem, nextShowStopTime.toString)
        // sendCommand(currentShowStartStopItem, currentShowStartStop.toString)
        sendCommand(nextShowStartStopItem, nextShowStartStop.toString)
        // sendCommand(logoURLItem, logoURL)
    ]

    // Calculate remaining show time for current runnning show
    gTVStopTime.members.forEach[ i |
        val currentShowDuration = i.name.split("_").get(0).toString+"_"+i.name.split("_").get(1).toString+"_"+i.name.split("_").get(2).toString+"_CurrentShowDuration"
        val oldDate = new DateTime((i.state as DateTimeType).calendar.timeInMillis).millis
        val newDate = new DateTime(now()).millis
        val duration = (((oldDate-newDate) / 1000) / 60)
        sendCommand(currentShowDuration, duration.toString)
    ]

    sendCommand(EPG_Update, 4) // Sending successfull value to update-item
    logInfo("EPG","EPG: openHAB-items succesfully updated! \u2713")
end // -------------------------------------------------------------> END

// -------------------------------------------------------------> START
//
//                          EPG Rule #5  
//
// This rule is very important to reduce the system calls to a
// minimum - it checks, if the stop-time of any of the current 
// running tv-shows is reached.
//
// -------------------------------------------------------------
rule "EPG - Check if an EPG update is needed"
when
    Time cron "0 0/1 * * * ?" // Execute every minute...
then
    // Check for 'epgsource_bynow.xml'
    val epgByNow = new File(xml_path + "data/epgsource_bynow.xml")
    if(epgByNow.isFile() && epgByNow.canRead()) { 

        // Calculate remaining show time for current runnning show
        gTVStopTime.members.forEach[ i |
            val currentShowDuration = i.name.split("_").get(0).toString+"_"+i.name.split("_").get(1).toString+"_"+i.name.split("_").get(2).toString+"_CurrentShowDuration"
            val oldDate = new DateTime((i.state as DateTimeType).calendar.timeInMillis).millis
            val newDate = new DateTime(now()).millis
            val duration = (((oldDate-newDate) / 1000) / 60)
            sendCommand(currentShowDuration, duration.toString)
        ]

        // Get timetsamp of the show with the earliest stoptime
        val stopTime = executeCommandLine("sudo python " + script_path + "epg_parser.py -t " + xml_path + "data/epgsource_bynow.xml -c get_stopextra",5000) 
        val DateTimeType timestamp = DateTimeType.valueOf(stopTime) // Convert human readable time stamp to DateTimeType

        // Compare current time with earliest stopDate of the current running shows
        if (now.isAfter(new DateTime(timestamp.zonedDateTime.toInstant.toEpochMilli)) ) { 
            executeCommandLine("sudo /usr/bin/tv_grep --on-after now --output " + xml_path + "data/epgsource_bynow.xml " + xml_path + "data/epgsource.xml",5000) // Update 'epgsource_bynow.xml'
            sendCommand(EPG_Update, 2) // execute rule #2
            logInfo("EPG", "EPG: A TV-show has ended. Starting update...")
        } else { return }
    } else {
        logError("EPG", "EPG Error: Update failed ('epgsource_bynow.xml' is missing or can´t be accessed)" )
        sendCommand(EPG_Update, 1) // return to rule #2
        return
    }
end // -------------------------------------------------------------> END

3.4. And thats what your EPG.sitemap could look like:

sitemap epg label="EPG"
{
    Frame label="" {
        Text item=TV_Channel_1_CurrentShow label="Das Erste [%s]" icon="tv_ard" {
            Frame item=TV_Channel_1_Name label="" {
                Text item=TV_Channel_1_CurrentShow icon="tv_ard"
                Text item=TV_Channel_1_CurrentShowDuration icon="time"
            }
        }
        Text item=TV_Channel_1_NextShow icon="next" {
            Frame item=TV_Channel_1_Name label="" {
                Text item=TV_Channel_1_NextShow label="Das Erste" icon="tv_ard"
                Text item=TV_Channel_1_NextShowStartStop
            }
        }
    }
    
    Frame label="" {
        Text item=TV_Channel_2_CurrentShow label="ZDF [%s]" icon="tv_zdf" {
            Frame item=TV_Channel_2_Name label="" {
                Text item=TV_Channel_2_CurrentShow icon="tv_zdf"
                Text item=TV_Channel_2_CurrentShowDuration icon="time"
            }
        }
        Text item=TV_Channel_2_NextShow icon="next" {
            Frame item=TV_Channel_2_Name label="" {
                Text item=TV_Channel_2_NextShow label="Das Erste" icon="tv_ard"
                Text item=TV_Channel_2_NextShowStartStop
            }
        }
    }
}

The rule should trigger an initial update automatically, after you created the above files.

If you find any errors or have improvements for anything, please tell me!

6 Likes

FYI a binding is on PR for the same :slight_smile:

Ahh, good to know! :slight_smile: Do you have any links with more infos on that - would love to know more. Thank you!

That’s a nice EPG in Basic UI @Simson! Now if only there was something good on TV. :wink:

Here’s a link to the binding PR:

You can also test it with this org.openhab.binding.xmltv-2.4.0-SNAPSHOT.jar build by Jenkins.

1 Like

Thanks for the link. Gave it a try and got some errors while trying to add a channel - but it´s still in developement, so maybe I just have to wait a bit more. :slight_smile:

For completeness this was my used *.things-file:

xmltv:XmlTVFile:bridge      [filePath="/etc/openhab2/html/epg/data/epgsource.xml", refresh=24]
xmltv:Channel:bridge:ARD    [channelId="hd.daserste.de", offset=0, refresh=60]

and the error in my log looks like…

2018-12-21 00:31:12.335 [WARN ] [mmon.WrappedScheduledExecutorService] - Scheduled runnable ended with an exception: 
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at java.util.ArrayList.rangeCheck(ArrayList.java:657) ~[?:?]
	at java.util.ArrayList.get(ArrayList.java:433) ~[?:?]
	at org.openhab.binding.xmltv.internal.handler.ChannelHandler.updateChannel(ChannelHandler.java:151) ~[?:?]
	at org.openhab.binding.xmltv.internal.handler.ChannelHandler.lambda$1(ChannelHandler.java:92) ~[?:?]
	at java.util.ArrayList.forEach(ArrayList.java:1257) ~[?:?]
	at java.util.Collections$UnmodifiableCollection.forEach(Collections.java:1080) ~[?:?]
	at org.openhab.binding.xmltv.internal.handler.ChannelHandler.lambda$0(ChannelHandler.java:92) ~[?:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:?]
	at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) ~[?:?]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) ~[?:?]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:?]
	at java.lang.Thread.run(Thread.java:748) [?:?]

Hey, really nice setup, I love it!
But is there an easier way to configure the needed channels?

Thanks for helping with testing! I’ve added a review comment for this at that line number.

Thanks for your kind words!
With easier configuration, you mean the configuration of xmltv? Or the OH-part of this setup (MAP, rules, items etc.)?

I think, there won´t be a much easier way to configure the channels until the mentioned binding is ready - even then, you have to configure each channel as a separate thing with a bunch of items connected.

The *.MAP in my example handles the channelIDs similar to what the binding does with the ‘Channel’-things.

1 Like

you’re welcome! :slight_smile:

Yeah, I meant the xmlTV channels…

sudo -u openhab /usr/bin/tv_grab_eu_egon --configure --config-file /etc/openhab2/html/epg/conf/epg_eu.conf

It’s not that bit of a fun clicking through so many channels
Maybe they’re backupd in a file and can be edited …

Yeah, it isn´t. I did that once and have regretted it. Round about 1.000 channels only for the EU region… :worried:
While configuring the grabber, you could type in “none” in the selection of the channels and edit the file much easier afterwards with your favourite editor:
/etc/openhab2/html/epg/conf/epg_eu.conf

Just exchange the ‘!’ with an ‘=’ to add a channel to your selection. But you still have to go through all the ~1000 lines of channels…

1 Like

how this binding will work if it is complete? You have to configure xmltv to run eg.: once per day and update that file which is given for OH as the source?

I don’t use it myself but if I read the README it looks like you’d have to download the file yourself.

Would it make sense to replace filePath with a URL and so it can support both file and HTTP URLs @glhopital ?

Yes, currently you have to place the file yourself, or by shell script in the dedicated place. It will be read every day

Not sure because there are many distinct ways to grab / build the XML file depending on the country where one is. Plus some are available online a ZIP, so at this stage, I prefered let this out of the binding.

while using the EPG.rules script I noticed running into a endless loop:
between the end of rule 1

    } else {
        // Ausführen falls epgsource.xml nicht auffind- oder bearbeitbar ist.
        logInfo("EPG","EPG: 'epgsource.xml ' ist nicht verfügbar oder bearbeitbar - holen von EPG-data...")
        executeCommandLine("sudo /usr/bin/" + xml_grabber + " --config-file " + xml_path + "conf/epg_eu.conf --days 2 --quiet --output " + xml_path + "data/epgsource.xml",5000)
        sendCommand(EPG_Update, 1) // continue with rule step #2
    }

and rule 2:

// -------------------------------------------------------------
rule "EPG - Minimiert XMLTV-Datei jede Stunde"
when
    Time cron "0 0 * * * ?" or // Ausführung in jeder Stunde...
    Item EPG_Update received update 1 // ... und falls Regel #1 einen Trigger sendet
then
    // Checkt verfügbarkeit von 'epgsource.xml'
    val epgSource = new File(xml_path + "data/epgsource.xml") 
    if(epgSource.isFile() && epgSource.canRead()) { 

        // Extrahiert alle Shows die jetzt laufen, hängt von der lokalen Zeit ab
        executeCommandLine("sudo /usr/bin/tv_grep --on-after now --output " + xml_path + "data/epgsource_bynow.xml " + xml_path + "data/epgsource.xml",5000)
        sendCommand(EPG_Update, 2) // weitermachen mit regel #3 
        logInfo("EPG","EPG: 'epgsource.xml' successfully minimized.")
    } else {
        sendCommand(EPG_Update, 0) // falls 'epgsource' nicht gefunden wurde - Zurück zu Regel #1...
        return
    }
end // -------------------------------------------------------------> END

epgsource.xml will never be created…
running the command manually gets me this output:


(every path is valid)
Looks like this command wants an input …

when editing

sudo nano /usr/bin/tv_grap_eu_egon

it’s actually completly empty, how can I get it to be filled with data?

Logfile while the Loop occurs:

Oh okay, thanks for the info with this speacial loop, when failing to create the epgsource.xml.

Theres a lot of garbage going on in this first version of my script - but i´m working on a much better (and faster) version of this right now (until the real binding gets released). which I´ll finish tommrrow, I hope.

But the error seems not related to the OH-rule… Did you install and configure xmltv already?

sudo apt-get install xmltv

The best way would be to wait until tommorrow, when I will update this post with a more detailed description and better version. :slight_smile:

1 Like

Cool, will rewrite it in Jython when I find time and finally be familiar with the JSR223 API! :slight_smile:

Yes of course, it’s the latest version.
I’ve even looked through your shell script and can’t find anything I didn’t do …

I’m for sure hyped to look into it :wink:

any updates?

creating the epcsource.xml worked I’ve just had a silly syntax fail:

sudo /usr/bin/tv_grab_eu_egon --config-file /etc/openhab2/html/epg/conf/epg_eu.conf --days 2 --quiet --output /etc/openhab2/html/epg/data/epgsource.xml

thankfully did it!

But I’m running into the next loop:

2019-01-10 13:07:09.277 [ERROR] [g.eclipse.smarthome.model.script.EPG] - EPG Error: Update fehlgeschlagen ('epgsource_bynow.xml' ist nicht vorhanden oder kann nicht bearbeitet werden)

==> /var/log/openhab2/events.log <==

2019-01-10 13:07:09.280 [ome.event.ItemCommandEvent] - Item 'EPG_Update' received command 1

2019-01-10 13:07:09.287 [vent.ItemStateChangedEvent] - EPG_Update changed from 2 to 1

==> /var/log/openhab2/openhab.log <==

2019-01-10 13:07:09.346 [INFO ] [g.eclipse.smarthome.model.script.EPG] - EPG: 'epgsource.xml' successfully minimized.

==> /var/log/openhab2/events.log <==

2019-01-10 13:07:09.350 [ome.event.ItemCommandEvent] - Item 'EPG_Update' received command 2

2019-01-10 13:07:09.354 [vent.ItemStateChangedEvent] - EPG_Update changed from 1 to 2

==> /var/log/openhab2/openhab.log <==

2019-01-10 13:07:09.360 [ERROR] [g.eclipse.smarthome.model.script.EPG] - EPG Error: Update fehlgeschlagen ('epgsource_bynow.xml' ist nicht vorhanden oder kann nicht bearbeitet werden)

==> /var/log/openhab2/events.log <==

2019-01-10 13:07:09.366 [ome.event.ItemCommandEvent] - Item 'EPG_Update' received command 1

2019-01-10 13:07:09.373 [vent.ItemStateChangedEvent] - EPG_Update changed from 2 to 1

==> /var/log/openhab2/openhab.log <==

2019-01-10 13:07:09.433 [INFO ] [g.eclipse.smarthome.model.script.EPG] - EPG: 'epgsource.xml' successfully minimized.

I’m gonna look into that.
Edit:
Same thing:

// Extrahiert alle Shows die jetzt laufen, hängt von der lokalen Zeit ab
        executeCommandLine("sudo /usr/bin/tv_grep --on-after now --output " + xml_path + 
"data/epgsource_bynow.xml " + xml_path + "data/epgsource.xml",5000)

Didn’t get executed, and fails at this if statement (obviously lol)

if(epgFiltered.isFile() && epgFiltered.canRead())

after running


the rule works fine!
Thanks, hope I could helped others implement xmltv!

Are there any plans to combine this with a triggered Switch that would allow me to switch off my Horizon box when it is not needed? Something similar to the CalDAV binding…?
Thanks!

Hi All i am trying to get the xmltv Binding to work at the moment i have installed it in OpenHAB2.5 build #1545 and it configure up correctly creates the channel thing from the xml file but fails to capture any additional real data is there something else i need to do to get the data into the channels