Philips 55PUS7303 – Android Smart TV per REST steuern

Bisher hatte ich meine Fernseher hauptsächlich als reines Anzeigegerät genutzt und „smarte Funktionen“ auf externe Geräte (wie meinen Apple TV oder einen Raspberry Pi) ausgelagert. Dieses Konzept hat die letzten Jahre gut funktioniert.

Da nun aber mein alter Fernseher vor einigen Monaten seinen Dienst aufgegeben hat (und er damit verdient in seine Rente gehen durfte), war ich auf der Suche nach einem neuen TV Gerät.

Die Wahl ist nach langer Überlegung letztlich auf einen Philips 55PUS7303 gefallen – ein Android TV. Meine Hoffnung durch Android ist eine längere Unterstützung von Firmware Updates vom Hersteller, bzw. später ggf. auch die Möglichkeit für eine Custom Firmware.

Einbindung in die Home Automatisierung.
Mit einem vollständigen Android ausgestattet war ich nun auf der Suche nach einer Lösung diesen Fernseher auch komplett in die Home Automatisierung einzubinden.

  • Gerät ein- und ausschalten
  • Lautstärke anpassen
  • Kanäle und Eingänge wechseln
  • Steuerung der Hintergrundbeleuchtung (Ambilights)

Die erste Überlegung in Sachen Android war das ganze über ADB (die Android Debug Bridge) zu lösen. Also ein passendes USB Kabel bestellt, den Entwickler Modus in den Einstellungen aktiviert und die ADB Kopplung gestartet. Am Ende konnte ich zumindest die Lautstärke problemlos regeln. Glücklich war ich mit dieser Lösung allerdings nicht, zumal ich auch gerne auf das Android SDK auf meiner Steuerzentrale verzichten wollen würde.

Nach weiterer Recherche bin ich auf ein Bash Script zur Steuerung von Philips TVs bei GitHub gestoßen.
Dieses Tool nutzt die sogenannte Jointspace API, mit der Philips TVs ausgestattet sind. Darüber kann nahezu jede Funktion ausgeführt werden, die sich auch mit der normalen Fernbedienung steuern lässt. Die API ist unter den folgenden TCP Ports auf dem Fernseher erreichbar.

  • Port 1925 – JointSpace HTTP
  • Port 1926 – JointSpace HTTPS

Authentifizierung über die API
Die API benötigt (ab der bei mir eingesetzt Version 6) für gewisse Funktionen einen gültigen Authentifizierung. Dabei handelt es sich um eine Device ID als Benutzernamen sowie einem zugehörigen Kennwort. Das Kennwort erhält man durch den Kopplungsprozess der Device ID, bei dem man lediglich den PIN vom Display des Fernseher ablesen muss. Das Bash Script übernimmt diesen Job und schreibt die Credentials dabei direkt in die Datei im aktuellen Verzeichnisses.

.credentials.tv

Diese Credentials kann man dann nutzen um sich mit der API zu verbinden. Einige Beispiele für die Verwendung findet man direkt im Script.

Um zum Beispiel die Lautstärke auf Mute zu stellen kann man folgenden cURL Aufruf verwenden.

-bash# curl -XPOST -u $DEVICEID:$PASSWORD https://10.0.0.34:1926/6/input/key -d "{'key': 'Mute'}" -k --digest -v

Wichtig ist das die Credentials im Digest Format übertragen werden müssen.

Eine API Dokumentation mit möglichen Kommandos findet man hier.

Anschalten des Geräts
Es gibt zwar auch die Möglichkeit den Fernseher über die API anzuschalten, allerdings schaltet der Fernseher nach einiger Zeit im Standby die Netzwerkfunktion ab. Dies ist für meinen Einsatz allerdings kein Problem. Der Fernseher hängt bei mir stromtechnisch an einer schaltbaren Homematic Funksteckdose und wird ohnehin nur bei Bedarf eingeschaltet. Sobald das Gerät Strom bekommt startet es automatisch und stellt den zuletzt bekannten Status bzw. Kanal wieder her.

Automatic reboots after Linux patches

Vor kurzen habe ich mich in einem Projekt um die Automatisierung von Linux Patches gekümmert. Nach einem Upgrade ist bekanntlich je nach Art der installierten Updates eventuell ein Restart von bestimmten Diensten oder sogar ein kompletter Reboot notwendig. Der Kunde wollte den Reboot nicht pauschalisieren und hatte dafür bereits eine Lösung parat. Folgenden Codeschnipsel habe ich vorgefunden.

#!/bin/bash
LAST_KERNEL=$(rpm -q --last kernel | perl -pe 's/^kernel-(\S+).*/$1/' | head -1)
CURRENT_KERNEL=$(uname -r)

test $LAST_KERNEL = $CURRENT_KERNEL || echo REBOOT

Kurz gesagt wird einfach geprüft ob das laufende System dem zuletzt installierten Kernel entspricht. Das mag zwar funktionieren, lässt aber viele Aspekte außer Acht. Ein Reboot macht nicht nur beim Update des Kernels, sondern zum Beispiel auch bei der Aktualisierung von Core Libraries (wie etwa glibc) Sinn. Dazu kommen weitere Pakete, die dann schon einiges mehr an Logik im Update Script benötigen.

Zum Glück ist das ganze aber kein neues Thema und wurde schon oftmals von anderen Leuten gelöst. Sowohl für Enterprise Linux als auch für Debian basierte Systeme gibt es jeweils eine sehr schöne und einfache, fertige Lösung.

Enterprise Linux
Für EL basierte Systeme (CentOS, RedHat) heißt der nötige Befehl needs-restarting und wird über das Paket yum-utils (ohnehin auf jedem System sinnvoll) ausgeliefert.

-bash# needs-restarting -r || shutdown -r

Eine erweiterte Möglichkeit bietet auch das YUM Plugin „ps“.

-bash# yum ps
Geladene Plugins: etckeeper, fastestmirror, ps
       pid proc                  CPU      RSS      State uptime
Loading mirror speeds from cached hostfile
 * base: mirror.checkdomain.de
 * epel: mirror.wiuwiu.de
 * extras: mirror.checkdomain.de
 * updates: mirror.checkdomain.de
kernel-3.10.0-862.6.3.el7.x86_64  
         0 <kernel>             0:00      0 B    Running: *4 day(s) 8:55:06

Dieses Plugin bricht das ganze auch auf einzelne Dienste herunter. Sofern also kein Reboot sondern nur ein Neustart eines bestimmten Dienstes notwendig sein sollte kann man das hiermit ebenfalls auslesen.

Debian
Für Debian und Ubuntu heißt das nötige Tool reboot-notifier und sollte meiner Meinung nach ebenfalls auf jedem System installiert sein. Sofern ein Reboot benötigt wird wird durch einen APT Hook das File /var/run/reboot-required angelegt.

[ -f /var/run/reboot-required ] && shutdown -r

Eine erweiterte Möglichkeit bietet checkrestart aus dem Paket debian-goodies.

-bash# checkrestart 
Found 25 processes using old versions of upgraded files
(16 distinct programs)
(11 distinct packages)

Of these, 10 seem to contain systemd service definitions or init scripts which can be used to restart them.
The following packages seem to have definitions that could be used
to restart their services:
uuid-runtime:
	1060	/usr/sbin/uuidd
irqbalance:
	1351	/usr/sbin/irqbalance
accountsservice:
	1382	/usr/lib/accountsservice/accounts-daemon
rsyslog:
	1408	/usr/sbin/rsyslogd
policykit-1:
	1515	/usr/lib/policykit-1/polkitd
openssh-server:
	2068	/usr/sbin/sshd
puppet:
	2546	/usr/bin/puppet
nagios-nrpe-server:
	4590	/usr/sbin/nrpe
ntp:
	4791	/usr/sbin/ntpd
fail2ban:
	5441	/usr/bin/fail2ban-server

These are the systemd services:
systemctl restart accounts-daemon.service
systemctl restart polkit.service

These are the initd scripts:
service uuidd restart
service irqbalance restart
service rsyslog restart
service ssh restart
service puppet restart
service nagios-nrpe-server restart
service ntp restart
service fail2ban restart

These processes (1) do not seem to have an associated init script to restart them:
qemu-system-x86:
	597	/usr/bin/qemu-system-x86_64
	6742	/usr/bin/qemu-system-x86_64
	6822	/usr/bin/qemu-system-x86_64
	6938	/usr/bin/qemu-system-x86_64
	26613	/usr/bin/qemu-system-x86_64
	26948	/usr/bin/qemu-system-x86_64
	31880	/usr/bin/qemu-system-x86_64
	32553	/usr/bin/qemu-system-x86_64
	32632	/usr/bin/qemu-system-x86_64

Upgrading your infrastructure to Puppet 4

Immer noch Puppet v3.x im Einsatz? Langsam wird es höchste Zeit auch deine letzten Systeme auf Puppet 4 umzustellen. Puppet 3 ist mittlerweile schon seit dem 31.12.2016 End of Life und viele Module aus der Forge sind längt nur mehr für Puppet 4 und aufwärts verfügbar.

In der Regel bedeutet die Umstellung von Puppet 3 auf Puppet 4 zwar eine Menge Arbeit, es lohnt sich aber definitiv. Welchen Performance Boost mit Puppet 4 beim Compiling eines Catalogs herausholen kann zeigt der folgende Auszug (aus Foreman) aus einem meiner aktuellen Projekte (ein Kunde mit ca. 1000 Puppet Nodes).

Upgrade zu Puppet 4

Die Grafik zeigt die Dauer eines Puppet Laufs – vor und nach dem Upgrade auf Puppet 4 der Node. Ein weiterer Boost mit dem nachgelagerten Upgrade auf Puppet 5.x steht noch aus.

Die Basis für das Upgrade auf Puppet 4 war dort insbesondere das weitreichende Testing des Codes via Jenkins CI Jobs und verschiedenen Tools. Dadurch konnten alle problematischen Stellen im Code gefunden und direkt ausgebessert werden. Einen ersten Überblick über die nötigen Änderungen kann der Puppet 4 Upgrade Guide geben.

Ein großartiges Tool für den Vergleich zwischen Katalogen („was passiert bei einem Upgrade auf v4?“) ist octocatalog-diff von GitHub.

Noch keine Strategie für das Upgrade auf Puppet 4.x? Setzt euch gerne mit mir in Verbindung und wir besprechen wie ich euch unterstützen kann.

Puppet: Distributing external facts (facts.d)

Am Montag habe ich bereits zum dritten mal mal meine Zertifizierung zum Certified Puppet Professional verlängert.

Puppet Certified Professional 2017

Eine Sache an die ich während des Tests wieder erinnert wurde, ist das Facter ab Version 3.9 auch die Möglichkeit bietet externe Facts komplett automatisch via Pluginsync zu verteilen.

[root@web01 ~]# puppet agent --test
Info: Using configured environment 'production'
Info: Retrieving pluginfacts
Notice: /File[/opt/puppetlabs/puppet/cache/facts.d/external_fact.sh]/content: 
--- /opt/puppetlabs/puppet/cache/facts.d/external_fact.sh	2017-11-01 23:27:31.049639111 +0100
+++ /tmp/puppet-file20171102-1070-p2fybh	2017-11-02 13:26:22.145274644 +0100
@@ -1,2 +1,3 @@
+ #!/bin/bash
+ echo "my_external_fact=true"

Notice: /File[/opt/puppetlabs/puppet/cache/facts.d/external_fact.sh]/content: content changed '{md5}5f133e40e41936739c335f63ec44537f' to '{md5}a752548b79b45d178df35d9b585e85fe'
Info: Retrieving plugin

External Facts sind Facts, die nicht direkt in Ruby geschrieben wurden. Dies ist eine schöne Möglichkeit eigene Facts zu entwicklen, ohne das man Kenntnisse in der Sprache Ruby benötigt. Die Facts müssen ledeglich in einem beliebigen Modul im Ordner „facts.d“ abgelegt werden. Möglich sind dabei alle denkbaren Sprachen (*.sh, *.pl, …) sowie statische Textdateien.

Auch Structured Facts sind möglich, wenn das Script die Ausgabe z.B. im JSON Format zurückliefert.

In früheren Versionen von Puppet musste man die External Facts als Dateien händisch via File Resource auf die Systeme verteilen. Dies hat allerdings den großen Nachteil das der Fact erst ab dem zweiten Puppetlauf zur Verfügung steht.

Ab Puppet 4 kann dies noch problematischer werden werden. Puppet 4 verhält sich in vielen Dingen deutlicher strikter und zwingt den User zu sauberen Puppet Code. Der Zugriff auf eine undefinierte Variable etwa wirft einen Catalog Error beim kompilieren.
Das ist zum Beispiel der Fall, wenn man im Puppet Manifest auf einen vermeintlich verfügbaren externen Fact referenziert, der allerdings erst über Puppet ausgerollt werden muss. Damit hat man sich dann unter Puppet 4 einen Deadlock geschaffen.

Zu beachten ist natürlich das External Facts langsamer abgearbeitet werden als jene Facts, die direkt in Ruby geschrieben wurden. Der Grund hierfür ist das für jeden Fact der entsprechende Shell Interpreter als eigener Subprozess unterhalb von Puppet geöffnet werden muss.

SAN Zertifikate mit der Microsoft CA

Auch mit der Microsoft CA (Certificate Authority) ist es möglich SAN Zertifikate über das Webenrollment (auch bekannt als „certsrv“ bekannt) auszustellen.

Hierzu muss jedoch vorher einmalig über die CMD folgender Befehl (mit anschließendem Neustart des Services) auf dem Host mit der PKI Rolle ausgeführt werden.

certutil -setreg policy\EditFlags +EDITF_ATTRIBUTESUBJECTALTNAME2
net stop certsvc
net start certsvc

Das Attribut für einen AltName kann direkt beim einreichen des Requests im Webinterface über das dafür vorhergesehene Feld angegeben werden. Der Syntax ist dabei wie folgt.

Microsoft CA - SAN Zertifikat

san:dns=name1.example.com[&dns=name2.example.com]

Ohne den vorherigen CMD Befehl wird der Parameter für einen AltName komplett ignoriert.