Vu+ Duo 4K SE BT initial bringup

Table of Contents

My existing Enigma2 receiver, a Dreamdbox DM900 UHD, has been replaced by a Vu+ Duo4K SE.

This is my braindump of the initial bringup.

Why Decommission the DM900 UHD

A month or three ago, my DM900 UHD’s display stopped displaying pictures, from powerup on it would light its backlight but only display a white image :-(

This behaviour is not uncommon (German page).

Sadly, replacing both the display and the controlled board, did not help. So a new Linux based settop box was in the cards.

While I did try MythTV in the past, I am a long time Enigma2 (E2) user and before that was using VDR. Having used a certain interface for decades, I prefer hardware that can run E2 and is silent.

Which Distro to Pick

I started by looking at distros under the OE-Alliance.

There OpenBh caught my eye because

root@vuduo4kse:~# python --version
Python 3.9.7
root@vuduo4kse:~# python2 --version
-bash: python2: command not found

Which Hardware to Pick

Once I had picked a distro, it was time to choose a hardware vendor.

My requirements a probably not common, but they are as follows

  • have a display so I need not switch on the TV for some actions
  • be able to ssh into the box
  • run a Linux distro
  • able to wake from deep sleep for a recording
  • able to handle at least 2 DVB-C streams, so that I can record one channel while watching another
  • able to accept a local SATA storage device for recordings
  • the ability to add software of my choosing to the running OS

Bonus points for

  • supporting Wake on LAN so I can power up the box without walking to it
  • concurrently recording/showing more than 2 DVB-C streams
  • the ability to also receive DVB-T2, again 2 channels at the same time as a minimum
  • Gigabit Ethernet rather than 100 Mbit/s
  • using a packaging system, I do not miss the days of ./configure && make && make install at all
  • offering a repository with pre-made packages

Why a Vu+ Duo 4K SE

With the above requirements, I settled (more or less on a whim) at a VU+ Duo 4K SE BT FBC Twin PVR 1xC/T2 bk 1xDVB-C/1xDVB-T2 FBC Twin Tuner PVR BT. While I currently have no DVB-T2 antenna in use, I want to be able to use one in addition to my current DVB-C feed.

That model also has plenty of RAM and flash space for now with 3 GiB RAM and 4GiB eMMC flash coupled with 4 ARMv7 cores at 1500MHz.

I live in Europe and thus have 2 weeks to send back any on-line orders, so while my choice was more expensive than quite a few of the alternatives, the hardware vendor needs to make enough profit to stay around for the years to come and if it all turned out to be horrible, I could just return to the German reseller I chose.

It was a bit odd that, at the end of February 2022, the English Vu+ site did not yet list the Duo 4K SE BT in their products list, it only lists the older non SE model. But well, the vendor is from South Korea so maybe they are just a bit slow with the foreign language site updates. It is listed on Wikipedia as available since 2020. It is listed on the Vu+ image download server. It is listed at Satco Europe. Since Satco Europe does not sell to private end users, I bought mine from ALTERNATE GmbH, a shop I have been using happily for a while now.

The one thing I did not look at was Linux mainline support for the tuners, that was dumb of me :-(

Verify that the Box is functional

This was done with the preinstalled Vu+ image. Nothing to report; the expected E2 initial bringup wizard came up on first boot, I went through the steps and watched a few minutes of live TV to ensure the box works before reflashing it.

Install OpenBh 5.0

Now since I wanted a recent, maintained, sources easily available distro, I Installed OpenBh 5.0.

I grabbed the archive for my model and installed with the refreshingly straightforward software update functionality; unpack an archive to a USB stick, keeping the folder structure inside the archive! The Vu+ picks it up on cold boot and you confirm via a button press on the box. This is guided by the box’ display. If you do not confirm, then the box boots normally. Nice, I wish all hardware was this flash friendly. No mucking about with fastboot, boot button pressese or LARTing the hardware into understanbding that when I bought a pice of hardware, then I expect to have control over which OS is installed on it.

This gets the brand Vu+ a huge plus in my book.

Configuration Steps Taken

Not that I really need CrossEPG, the EPG coming over DVB-C has been good in this location with my previous E2 boxes, but I did want to try it.

For those that do not understand German, the post roughly translates to;

Execute Update Rytec Providers once to get a full XMLTV providers list, then select your region under XMLTV providers

In my case, I chose the region Deutschland/Osterreich/Switzerland - Gemeinsam (xz).

Use Ansible to Setup the Enigma 2 box to Autodelete Recordings

Why on Earth Would I use Ansible for This

My primary use case of Enignma 2 is that it allows me switch it on at a time of my choosing and find the latest news available for watching. While this is easily achieved with Open Bh 5.0’s AutoTimers function, I also want to be able to have older recordings deleted after a while.

As such, like on all my previous live TV recording setups, I cobbled something together. This time, instead of having to dig out yet another E2 forum post of mine each time I flash a different distribution on my currently used STB and then do some copypasta, I used a template, some inventory entries and a playbook simply because i can™ :-).

Template to Create the Autoclean Script

The template simply iterates over 3 lists (populated in inventory);

  • shows I want to delete if they are older than 2 days
  • shows I want to delete if they are older than 7 days
  • shows I want to delete if they are older than 28 days

to create some entries along the lines of

/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +2 -name "*Das Erste HD - Tagesschau*" -print -delete
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +7 -name "*Das Erste HD - FIFA WM*" -print -delete
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +28 -name "*Das Erste HD - ttt - titel thesen temperamente*" -print -delete

followed up with a run of the extremely useful cleanup.py from jbretsch’s pvrutils. That allows for my /media/hdd/movie/autoclean to never fill up, even if I record shows not explicitly deleted after N days.

templates/Enigma 2/E2-auto-clean-script.bash.j2 reads

#!/bin/bash
#
# {{ ansible_managed }}
#
# make subdirs in /media/hdd/movie/
#   /media/hdd/movie/autoclean
#   /media/hdd/movie/keep (remember to set this as your default Enignma2 recording path)
#
# This script will is meant to remove recordings after a set amont of days by using the
# find command with the delete flag. So it should contain lines like the following:
# /usr/bin/find  /media/hdd/movie/autoclean -maxdepth 1 -mtime +2 -name "*Das Erste HD - Tagesschau*" -delete
#
# The idea is that e.g. news recorded every day are really only interesting for a couple days,
# so one might as well delete them after two days.
#
# See the thread https://dreambox.de/board/index.php?thread/17617-automatisches-l%C3%B6schen-von-%C3%A4lteren-serien/
#

date

echo disk space before cleaning
df -h /media/hdd/movie/autoclean

# {{ ansible_managed }}

# begin of automatically constructed lines

# iff (if and only if) e2_autoclean_after_…days (02, 07 and 28) are defined in Ansible inventory
# then the following three blocks will be populated
# please be aware that you can easily shoot yourself in the foot with 'find … -delete',
# so review the generated script before running it!

echo "deleting the following shows, whose recordings are older than 2 days:"
# iff the list e2_autoclean_after_02days is populated in Ansible inventory, then we will get lines here
{% for e2_autoclean_pattern_02d in e2_autoclean_after_02days %}
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +2 -name "{{ e2_autoclean_pattern_02d }}" -print -delete
{% endfor %}
echo

echo "deleting the following shows, whose recordings are older than 7 days:"
# iff the list e2_autoclean_after_07days is populated in Ansible inventory, then we will get lines here
{% for e2_autoclean_pattern_07d in e2_autoclean_after_07days %}
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +7 -name "{{ e2_autoclean_pattern_07d }}" -print -delete
{% endfor %}
echo

echo "deleting the following shows, whose recordings are older than 28 days:"
# iff the list e2_autoclean_after_28days is populated in Ansible inventory, then we will get lines here
{% for e2_autoclean_pattern_28d in e2_autoclean_after_28days %}
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +28 -name "{{ e2_autoclean_pattern_28d }}" -print -delete
{% endfor %}
echo

# end of automatically constructed lines

echo "running cleanup.py from https://github.com/jbretsch/pvrutils"
# keep 100GB free
/home/root/bin/cleanup.py -s 102400 /media/hdd/movie/autoclean

echo disk space after cleaning
df -h /media/hdd/movie/autoclean
echo

echo "all done"
date

Inventory Entries

In my hosts file (yes, I know, this would be cleaner as a host_var);

vuduo4kse                           ansible_host=vuduo4kse.internal.pcfe.net                    ansible_user=root

And in host_vars/vuduo4kse/E2-auto-clean-script.yml I define the 3 lists for 2, 7 and 28 days retention.

# The filenames of recordings will be in the form
# /media/hdd/movie/autoclean/20220315 1958 - Das Erste HD - Tagesschau.eit
# /media/hdd/movie/autoclean/20220315 1958 - Das Erste HD - Tagesschau.ts.sc
# /media/hdd/movie/autoclean/20220315 1958 - Das Erste HD - Tagesschau.ts.cuts
# /media/hdd/movie/autoclean/20220315 1958 - Das Erste HD - Tagesschau.ts.ap
# /media/hdd/movie/autoclean/20220315 1958 - Das Erste HD - Tagesschau.ts
# /media/hdd/movie/autoclean/20220315 1958 - Das Erste HD - Tagesschau.ts.meta
#
# So for the autoclean after N days lists below, you need to carefully test first with e.g.
# /usr/bin/find  /media/hdd/movie/autoclean -maxdepth 1  -name "*heute*"
# when deciding on the patterns you list here.
# Obviously, the more generic your entry (e.g. '*heute*'), the greater the risk that you delete more than you bargained for,
# so it is preferable to use something more specific (e.g. '*Das Erste HD - Tagesschau*')

# The three below lists will be used to construct a script containing lines like  the following
# /usr/bin/find  /media/hdd/movie/autoclean -maxdepth 1 -mtime +2 -name "*rbb Berlin HD - Abendschau*"
# but with the -delete option added.

e2_autoclean_after_02days:
  - '*rbb Berlin HD - Abendschau*'
  - '*Das Erste HD - Tagesschau*'
  - '*Das Erste HD - Tagesthemen*'
  - '*Das Erste HD - Nachtmagazin*'
  - '*Das Erste HD - Weltspiegel*'
  - '*Das Erste HD - Wetter vor acht*'
  - '*Das Erste HD - Sportschau*'

e2_autoclean_after_07days:
  - '*Formel 1*'
  - '*Formula 1*'
  - '*Das Erste HD - FIFA WM*'
  - '*ZDF HD - ZDF WM live*'
  - '*Eishockey*'

e2_autoclean_after_28days:
  - '*Das Erste HD - ttt - titel thesen temperamente*'
  - '* - extra 3.*'
  - '* - heute-show.*'

The Playbook

Is really just a couple tasks to create directories used by E2-auto-clean-script.bash, construct the script, put it in place and do a (dirty, with a hardcoded path) copy task to have cleanup.py from jbretsch/pvrutils available on the Vu+.

---
- name: Ensure Enigma 2 on my Vu+ Duo 4K SE is set up they way I want
  hosts:
    - vuduo4kse

  # I connect as root, so no need for become
  become: false

  tasks:
    # n.b. root's homedir is in the unusual location '/home/root/', not the usual '/root/'.
    - name: Ensure /home/root/bin/ exists
      ansible.builtin.file:
        path: /home/root/bin
        state: directory
        mode: u=rwx,g=rx,o=rx
        group: root
        owner: root

    - name: Ensure the generated E2-auto-clean-script.bash exists and is current
      ansible.builtin.template:
        src: templates/Enigma 2/E2-auto-clean-script.bash.j2
        dest: /home/root/bin/E2-auto-clean-script.bash
        mode: u=rwx,g=rx,o=rx
        owner: root
        group: root

    - name: Ensure cleanup.py from https://github.com/jbretsch/pvrutils.git is copied over
      ansible.builtin.copy:
        src: /home/pcfe/work/git/github.com/pvrutils/cleanup.py
        dest: /home/root/bin/
        mode: u=rwx,g=rx,o=rx
        group: root
        owner: root

    # it is quite intentional that the 2 paths are hardcoded instead of variables (both here and in the template),
    # this Ansible construct to create a script, run by root, containing many lines of "find … -delete",
    # all that is already enough of a bespoke foot bazooka as it is.
    - name: Ensure /media/hdd/movie/autoclean/ exists
      ansible.builtin.file:
        path: /media/hdd/movie/autoclean
        state: directory
        mode: u=rwx,g=rx,o=rx
        group: root
        owner: root

    - name: Ensure /media/hdd/movie/keep/ exists
      ansible.builtin.file:
        path: /media/hdd/movie/keep
        state: directory
        mode: u=rwx,g=rx,o=rx
        group: root
        owner: root

    # Dont do that (add a cronjob with the cron ansible module), CronManager.py is fragile
    # either write to a file not parsed by CronManager.py or enter by hand in Enigma 2
    # https://github.com/opendroid-Team/enigma2-plugin-extensions-extrapanel/blob/master/usr/lib/enigma2/python/Plugins/Extensions/Infopanel/CronManager.py
    # - name: Ensure E2-auto-clean-script.bash is called by cron and visible in E2's Infopanel extension Cron Manager
    #   cron:
    #     minute: "15"
    #     hour: "19"
    #     user: root
    #     job: "/home/root/bin/E2-auto-clean-script.bash"
    #     cron_file: /etc/cron/crontabs/root

    # then https://dreambox.de/board/index.php?thread/17617-automatisches-l%C3%B6schen-von-%C3%A4lteren-serien/&postID=179202#post179202
    # but old SysV init style or on OpenBh's cron timers config section

I do not know yet if I’ll add more tasks to the playbook, for that I would have to first hunt down the files changed by my settings in the E2 TV interface and I’m not sure I will bother.

Example of Generated Script

Here is an example of a generated /home/root/bin/E2-auto-clean-script.bash;

#!/bin/bash
#
# This file is managed by Ansible. Manual changes will be overwritten at the next Ansible run.
#
# make subdirs in /media/hdd/movie/
#   /media/hdd/movie/autoclean
#   /media/hdd/movie/keep (remember to set this as your default Enignma2 recording path)
#
# This script will is meant to remove recordings after a set amont of days by using the
# find command with the delete flag. So it should contain lines like the following:
# /usr/bin/find  /media/hdd/movie/autoclean -maxdepth 1 -mtime +2 -name "*Das Erste HD - Tagesschau*" -delete
#
# The idea is that e.g. news recorded every day are really only interesting for a couple days,
# so one might as well delete them after two, seven or 28 days.
#
# See the threads
# https://dreambox.de/board/index.php?thread/17617-automatisches-l%C3%B6schen-von-%C3%A4lteren-serien/
# https://board.openbh.net/threads/automatic-deletion-of-recordings-possible-in-openbh-5-0.784/

date

echo disk space before cleaning
df -h /media/hdd/movie/autoclean

# This file is managed by Ansible. Manual changes will be overwritten at the next Ansible run.

# begin of automatically constructed lines

# iff (if and only if) e2_autoclean_after_…days (02, 07 and 28) are defined in Ansible inventory
# then the following three blocks will be populated
# please be aware that you can easily shoot yourself in the foot with 'find … -delete',
# so review the generated script before running it!

echo "deleting the following shows, whose recordings are older than 2 days:"
# iff the list e2_autoclean_after_02days is populated in Ansible inventory, then we will get lines here
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +2 -name "*rbb Berlin HD - Abendschau*" -print -delete
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +2 -name "*Das Erste HD - Tagesschau*" -print -delete
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +2 -name "*Das Erste HD - Tagesthemen*" -print -delete
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +2 -name "*Das Erste HD - Nachtmagazin*" -print -delete
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +2 -name "*Das Erste HD - Weltspiegel*" -print -delete
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +2 -name "*Das Erste HD - Wetter vor acht*" -print -delete
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +2 -name "*Das Erste HD - Sportschau*" -print -delete
echo

echo "deleting the following shows, whose recordings are older than 7 days:"
# iff the list e2_autoclean_after_07days is populated in Ansible inventory, then we will get lines here
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +7 -name "*Formel 1*" -print -delete
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +7 -name "*Formula 1*" -print -delete
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +7 -name "*Das Erste HD - FIFA WM*" -print -delete
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +7 -name "*ZDF HD - ZDF WM live*" -print -delete
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +7 -name "*Eishockey*" -print -delete
echo

echo "deleting the following shows, whose recordings are older than 28 days:"
# iff the list e2_autoclean_after_28days is populated in Ansible inventory, then we will get lines here
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +28 -name "*Das Erste HD - ttt - titel thesen temperamente*" -print -delete
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +28 -name "* - extra 3.*" -print -delete
/usr/bin/find /media/hdd/movie/autoclean -maxdepth 1 -mtime +28 -name "* - heute-show.*" -print -delete
echo

# end of automatically constructed lines

echo "running cleanup.py from https://github.com/jbretsch/pvrutils"
# keep 100GB free
/home/root/bin/cleanup.py -s 102400 /media/hdd/movie/autoclean

echo disk space after cleaning
df -h /media/hdd/movie/autoclean
echo

echo "all done"
date