Achtung: Diese Seite enthält Partner- und Werbe-Links. Daher ist diese Seite im Gesamten als Werbeanzeige zu verstehen!
In meinem Blog-Artikel über mein Smart Home auf Basis von OpenHAB hatte ich bereits angekündigt, dass ich damit beginnen möchte, meine Teufel-Raumfeld-Boxen in das System einzubinden. Nach einigen Abenden Arbeit kann ich behaupten, dass ich mein Raumfeld-System so wie ich das gerne hätte, über OpenHAB steuern kann - und zwar inklusive Rückkanal per http-binding.
Die smarten Boxen von Teufel können zwar schon von Haus aus per WLAN gesteuert werden, die Steuerung über den UPnP-Standard ist aber eher umständlich und vor allem ist das Finden der richtigen, internen Web-Services nicht leicht, eben auch, weil auf einer Box meist mehrere davon laufen.
Die Raumfeld-Community hat dieses Problem aber schon mit Raumserver gelöst. Der Raumserver, den es mittlerweile auch als node.js-Version gibt, bietet eine einfachere Web-Schnittstelle zum Steuern meiner Boxen an und erledigt das Suchen der Raumfeld-Geräte intern. Diese Installationsanleitung auf GitHub hat in meinem Fall funktioniert, mittelfristig ist aber wohl der Umstieg auf die node.js-Version zu empfehlen.
Da ich meine Instanz des Raumservers mit OpenHAB auf einem Raspberry Pi betreibe und beide Softwarelösungen standardmäßig einen Web-Server auf Port 8080 betreiben wollen, muss einer davon angepasst werden. Ich habe darum den Port meiner OpenHAB-Instanz mit folgender Konfiguration auf Port 1337 geändert:
/etc/default/openhab2
EXTRA_JAVA_OPTS=""
OPENHAB_HTTP_PORT=1337
OPENHAB_HTTPS_PORT=1338
Dank dem http-binding kann ich mir das Erstellen der Things sparen und direkt entsprechende Items erstellen. Für jeden Raum und jede Box ein Item für die abzuspielende Musik, die Lautstärke sowie Schalter für "weiter", "zurück" und das Aktivieren des Schlaf-Timers. Hier die Item-Konfiguration meines Büros:
items/raumfeld_buero.items
Switch item_buero_raumfeld_stereo_s_music "Büro Box Raumfeld Stereo S Musik" (raumfeld_music) ["Switchable"]
Switch item_buero_raumfeld_stereo_s_mute "Büro Box Raumfeld Stereo S Lautlos" (raumfeld_mute) ["Switchable"] { http="<[item_all_raumfeld_http_cache:7500:JS(getBueroMute.js)]" }
Switch item_buero_raumfeld_stereo_s_next_track "Büro Box Raumfeld Stereo S Nächstes Lied" (raumfeld_next_track) ["Switchable"]
Switch item_buero_raumfeld_stereo_s_previous_track "Büro Box Raumfeld Stereo S Vorheriges Lied" (raumfeld_previous_track) ["Switchable"]
Switch item_buero_raumfeld_stereo_s_sleep_timer "Büro Box Raumfeld Stereo S Sleep-Timer" (raumfeld_sleep_timer) ["Switchable"]
Dimmer item_buero_raumfeld_stereo_s_volume "Büro Box Raumfeld Stereo S Lautstärke" (raumfeld_volume) ["Lighting"] { http="<[item_all_raumfeld_http_cache:7500:JS(getBueroVolume.js)]" }
Das { http="<[item_all_raumfeld_http_cache:7500:JS(getBueroVolume.js)]" }
bildet in diesem Fall den Rückkanal zu OpenHAB ab. Oder anders formuliert: Das System ruft regelmäßig eine URL des Raumserver auf, um die aktuell gesetzte Lautstärke der Box zu ermitteln, da ich diese schließlich auch über den Regler an der Box selbst oder die Raumfeld-App von Teufel ändern könnte. Das item_all_raumfeld_http_cache
stellt den Namen des im http-binding konfigurierten Webseiten-Aufruf dar:
services/http.cfg
item_all_raumfeld_http_cache.url=http://192.168.0.133:8080/raumserver/data/getRendererState?id=Wohnzimmer
item_all_raumfeld_http_cache.updateInterval=2500
Der Aufruf der URL http://192.168.0.133:8080/raumserver/data/getRendererState?id=Wohnzimmer
liefert alle von mir benötigten Informationen, also die Lautstärke und den "Lautlos"-Status jedes Raumes zurück. Außerdem enthält die Antwort auch Daten darüber, was derzeit auf dem angegebenen Abspielgerät - also Wohnzimmer
- abgespielt wird. Ich höre immer in allen Räumen die selbe Musik, darum ist es in meinem Fall egal, welchen Raum ich hier übergebe.
http://192.168.0.133:8080/raumserver/data/getRendererState?id=Wohnzimmer
[
{
udn:"uuid:DF76E0C9-9FB5-4856-BE9C-E7A194C7F9BE",
friendlyName:"Wohnzimmer,Büro,Fitness,Bad,Bett,Esszimmer,Küche,Treppe",
isZoneRenderer:true,
avTransportUri:"spotify-playlist://playback",
bitrate:320,
volume:45,
numberOfTracks:1,
currentTrack:1,
currentTrackDuration:300,
muteState:"NONE",
playMode:"RANDOM",
transportState:"STOPPED",
mediaItem:{
id:"0/Spotify/Track",
parentId:"0/Spotify",
type:"TRACK",
artist:"Knochenfabrik",
artistArtUri:"http://o.scdn.co/320/2d52764943b73da1aabd84a026ca2ad5a623da76",
album:"Ameisenstaat",
albumArtUri:"http://o.scdn.co/320/2d52764943b73da1aabd84a026ca2ad5a623da76",
albumDate:"",
albumTotalPlaytime:"",
albumTrackCount:0,
title:"Obdachlos & trotzdem sexy"
},
roomStates:[
{
roomUdn:"uuid:07b4b3f9-fd96-4a5d-80db-e89c650edcbf",
isMute:false,
isOnline:true,
volume:24,
transportState:"STOPPED"
},
{
roomUdn:"uuid:dcf5506a-ed56-439e-a166-07fca1afa6fa",
isMute:false,
isOnline:true,
volume:31,
transportState:"STOPPED"
},
{
roomUdn:"uuid:0eacbbc0-61f6-43d3-8edc-b881c30269d3",
isMute:false,
isOnline:true,
volume:27,
transportState:"STOPPED"
},
{
roomUdn:"uuid:238ee9e9-505d-4858-b0c9-b74efe2c254d",
isMute:false,
isOnline:true,
volume:28,
transportState:"STOPPED"
},
{
roomUdn:"uuid:2f83022b-9dfe-43cb-b4d2-428141c01411",
isMute:false,
isOnline:true,
volume:22,
transportState:"STOPPED"
},
{
roomUdn:"uuid:f58a1b19-8052-4275-8907-e66a614cc771",
isMute:false,
isOnline:true,
volume:27,
transportState:"STOPPED"
},
{
roomUdn:"uuid:9175f1a5-152f-428f-aeb0-9110736061d3",
isMute:false,
isOnline:true,
volume:45,
transportState:"STOPPED"
},
{
roomUdn:"uuid:0bdde71b-1083-4cbc-879e-57030d73d730",
isMute:false,
isOnline:true,
volume:22,
transportState:"STOPPED"
}
]
}
]
Die roomUdn
- also die ID - meiner Box im Büro - genauer: Dem darauf laufenden "Abspiel-Service" -s ist uuid:9175f1a5-152f-428f-aeb0-9110736061d3
. Ich benötige also folgenden Eintrag - oder genauer: das Feld volume
- aus der Antwort des Raumservers:
http://192.168.0.133:8080/raumserver/data/getRendererState?id=Wohnzimmer
{
roomUdn:"uuid:9175f1a5-152f-428f-aeb0-9110736061d3",
isMute:false,
isOnline:true,
volume:45,
transportState:"STOPPED"
}
OpenHAB benötigt genau genommen nur den Wert selbst, also 45
. Um das zu ermöglichen, muss eine Transformation eingesetzt werden. Zuerst hatte ich eine JsonPath-Transformation im Einsatz, kurz darauf habe ich dann aufgrund eines "Overflow"-Problems zu einer komplexeren Javascript-Transformation gegriffen.
Der Grund war, dass die Lautstärke vom Raumserver oder den Raumfeld-Boxen kurzzeitig als 255
oder 254
zurückgegeben wird, wenn die Lautstärke über den großen Regler der Raumfeld-App von Teufel geregelt wird. Hier werden alle Räume relativ zur Reglerbewegung verstellt und die Lautstärke schwappt dann wohl von 0
auf -1
, was dann irgendwo aufgrund eines Überlaufes zu 255
oder 254
wird. Mit der Folge, dass meine Boxen auf die maximale Lautstärke gestellt wurden, sobald man versucht hat, sie sehr leise zu stellen. Wer die Teufel-Boxen kennt, weiß, dass das vor allem gegen Mitternacht nicht zwingend zur Förderung der Nachbarschaftsbeziehungen beiträgt.
transform/getBueroVolume.js
(
function (input) {
var parsedInput = JSON.parse(input);
var roomStates = parsedInput[0].roomStates;
for (var key in roomStates) {
var currentRoomState = roomStates[key];
if (currentRoomState.roomUdn == 'uuid:9175f1a5-152f-428f-aeb0-9110736061d3') {
if (currentRoomState.volume > 100) {
currentRoomState.volume = 0;
} else if (currentRoomState.volume > 60) {
currentRoomState.volume = 60;
}
return Math.round(Math.min(Math.max(currentRoomState.volume, 0), 60));
}
}
return 0;
}
)(input);
Dieser Transformator iteriert über alle Räume, sucht die Büro-Box mit der roomUdn
uuid:9175f1a5-152f-428f-aeb0-9110736061d
und reduziert dann die Rückgabe auf die Lautstärke. Hierbei wird das Overflow-Problem abgefangen sowie die maximale Lautstärke auf 60% gedeckelt. Die Teufel-Boxen sind einfach viel zu mächtig, um sie in einer Mietwohnung weiter aufdrehen zu können.
Das Steuern der Boxen ist im Verhältnis zur Anbindung der Daten relativ einfach. Der Raumserver bietet hierfür einfache Schnittstellen, um alle benötigten Kommandos mit einem einfachen Webseiten-Aufruf zu steuern. Konkret benötige ich also nur ein paar Rules, die auf Veränderungen meiner zuvor erstellten Items hören:
rules/raumfeld_buero.rules
rule "Büro Box Raumfeld Stereo S Musik an"
when
Item item_buero_raumfeld_stereo_s_music changed to ON
then
sendHttpGetRequest("http://192.168.0.133:8080/raumserver/controller/play?id=B%C3%BCro");
end
rule "Büro Box Raumfeld Stereo S Musik aus"
when
Item item_buero_raumfeld_stereo_s_music changed to OFF
then
sendHttpGetRequest("http://192.168.0.133:8080/raumserver/controller/pause?id=B%C3%BCro");
end
rule "Büro Box Raumfeld Stereo S Lautstärke"
when
Item item_buero_raumfeld_stereo_s_volume changed
then
sendHttpGetRequest("http://192.168.0.133:8080/raumserver/controller/setVolume?id=B%C3%BCro&value=" + item_buero_raumfeld_stereo_s_volume.state.toString());
end
rule "Büro Box Raumfeld Stereo S Lautlos an"
when
Item item_buero_raumfeld_stereo_s_mute changed to ON
then
sendHttpGetRequest("http://192.168.0.133:8080/raumserver/controller/mute?id=B%C3%BCro");
end
rule "Büro Box Raumfeld Stereo S Lautlos aus"
when
Item item_buero_raumfeld_stereo_s_mute changed to OFF
then
sendHttpGetRequest("http://192.168.0.133:8080/raumserver/controller/unmute?id=B%C3%BCro");
end
Aktiviere ich beispielsweise den Schalter item_buero_raumfeld_stereo_s_mute
sodass er auf ON
steht, so erkennt das die Regel "Büro Box Raumfeld Stereo S Lautlos an" und ruft die Raumserver-URL http://192.168.0.133:8080/raumserver/controller/mute?id=B%C3%BCro
auf, die letztlich die Boxen im Büro auf "lautlos" stellt.
Ich pflege grundsätzlich alle möglichen Schalter und Texte in meine gerätebezogene Sitemap, darum enthält meine Sitemap für meine Teufel Boxen neben einer allgemeinen Steuerung auch eine spezifische Steuerung der Lautstärke pro Raum sowie eine handvoll Soundcloud-Playlists und Lautstärke-Voreinstellungen. Wenn ich morgens in mein Büro komme, kann ich die komplette Wohnung, mit Ausnahme meines Büros, lautlos stellen, damit meine Freundin entsprechend weiterschlafen kann. Außerdem wird noch angezeigt, welcher Titel, welcher Interpret und welches Album aktuell abgespielt wird:
Die interne Konfiguration ist letztlich folgende - ich habe ein paar Schalter und Dinge, die ich gar nicht benötigte, direkt wieder mit zwei anführenden //
auskommentiert, sodass diese nicht angezeigt werden:
sitemap/raumfeld.sitemap
sitemap raumfeld label="Raumfeld" {
Frame label="Now playing" {
Text item=item_all_raumfeld_artist label="Interpret"
Text item=item_all_raumfeld_title label="Titel"
Text item=item_all_raumfeld_album label="Album"
}
//Frame {
// Image item=item_all_raumfeld_album_image_url label="Cover" refresh=5
//}
Frame label="Alle Boxen" {
Default item=item_all_raumfeld_player label="Player"
//Switch item=item_all_raumfeld_music label="Musik" icon="soundvolume"
//Switch item=item_all_raumfeld_mute label="Lautlos" icon="soundvolume"
//Switch item=item_all_raumfeld_next_track label="Nächstes Lied" icon="switch"
//Switch item=item_all_raumfeld_previous_track label="Vorheriges Lied" icon="switch"
Switch item=item_all_raumfeld_sleep_timer label="Sleep-Timer (30 Minuten)" icon="switch"
Slider item=item_all_raumfeld_volume label="Lautstärke" icon="soundvolume" switchSupport
//Switch item=item_all_raumfeld_play_mode label="Modus" mappings=["NORMAL"="Normal", "SHUFFLE"="Shuffle", "REPEAT_ONE"="Ein Lied wiederholen", "REPEAT_ALL"="Alles wiederholen", "RANDOM"="Zufall"]
Switch item=item_all_raumfeld_play_mode label="Modus" mappings=["REPEAT_ALL"="Normal", "RANDOM"="Zufall"]
}
Frame label="Playlists" {
Switch item=item_all_raumfeld_playlist_anal label="Soundcloud: A.N.A.L"
Switch item=item_all_raumfeld_playlist_chill label="Soundcloud: Chill"
Switch item=item_all_raumfeld_playlist_deutschrap label="Soundcloud: Deutschrap"
Switch item=item_all_raumfeld_playlist_goa label="Soundcloud: Goa"
Switch item=item_all_raumfeld_playlist_happy label="Soundcloud: Happy"
Switch item=item_all_raumfeld_playlist_orientalisch label="Soundcloud: Orientalisch"
Switch item=item_all_raumfeld_playlist_pep label="Soundcloud: Pëp"
Switch item=item_all_raumfeld_playlist_random label="Soundcloud: Random"
Switch item=item_all_raumfeld_playlist_reggae label="Soundcloud: Reggae"
//Switch item=item_all_raumfeld_playlist_techno_tracks label="Soundcloud: Techno (Tracks)"
Switch item=item_all_raumfeld_playlist_techno_sets label="Soundcloud: Techno"
}
Frame label="Lautstärke" {
Switch item=item_all_raumfeld_volume_1 label="Alle Räume 1%"
Switch item=item_all_raumfeld_volume_10 label="Alle Räume 10%"
Switch item=item_all_raumfeld_volume_15 label="Alle Räume 15%"
Switch item=item_all_raumfeld_volume_20 label="Alle Räume 20%"
Switch item=item_all_raumfeld_volume_25 label="Alle Räume 25%"
Switch item=item_all_raumfeld_volume_30 label="Alle Räume 30%"
Switch item=item_all_raumfeld_volume_35 label="Alle Räume 35%"
Switch item=item_all_raumfeld_volume_40 label="Alle Räume 40%"
Switch item=item_all_raumfeld_volume_45 label="Alle Räume 45%"
Switch item=item_all_raumfeld_volume_50 label="Alle Räume 50%"
Switch item=item_all_raumfeld_volume_nur_bett_1 label="Nur Bett: 1%"
Switch item=item_all_raumfeld_volume_nur_buero_30 label="Nur Büro: 30%"
Switch item=item_all_raumfeld_volume_nur_buero_fitness_30 label="Nur Büro und Fitness: 30%"
Switch item=item_all_raumfeld_volume_nur_buero_fitness_60 label="Nur Büro und Fitness: 60%"
}
Frame label="Bad" {
Switch item=item_bad_raumfeld_one_s_music label="Musik" icon="soundvolume"
Switch item=item_bad_raumfeld_one_s_mute label="Lautlos" icon="soundvolume"
Slider item=item_bad_raumfeld_one_s_volume label="Lautstärke" icon="soundvolume" switchSupport
Text item=item_bad_raumfeld_one_s_volume label="Lautstärke [%s%%]"
Switch item=item_bett_raumfeld_one_s_volume label="" icon="soundvolume" mappings=["10"="10%", "20"="20%", "30"="30%", "40"="40%", "50"="50%", "60"="60%"]
}
Frame label="Bett" {
Switch item=item_bett_raumfeld_one_s_music label="Musik" icon="soundvolume"
Switch item=item_bett_raumfeld_one_s_mute label="Lautlos" icon="soundvolume"
Slider item=item_bett_raumfeld_one_s_volume label="Lautstärke" icon="soundvolume" switchSupport
Text item=item_bett_raumfeld_one_s_volume label="Lautstärke [%s%%]"
Switch item=item_bett_raumfeld_one_s_volume label="" icon="soundvolume" mappings=["10"="10%", "20"="20%", "30"="30%", "40"="40%", "50"="50%", "60"="60%"]
}
Frame label="Büro" {
Switch item=item_buero_raumfeld_stereo_s_music label="Musik" icon="soundvolume"
Switch item=item_buero_raumfeld_stereo_s_mute label="Lautlos" icon="soundvolume"
Slider item=item_buero_raumfeld_stereo_s_volume label="Lautstärke" icon="soundvolume" switchSupport
Text item=item_buero_raumfeld_stereo_s_volume label="Lautstärke [%s%%]"
Switch item=item_buero_raumfeld_stereo_s_volume label="" icon="soundvolume" mappings=["10"="10%", "20"="20%", "30"="30%", "40"="40%", "50"="50%", "60"="60%"]
}
Frame label="Esszimmer" {
Switch item=item_esszimmer_raumfeld_stereo_s_music label="Musik" icon="soundvolume"
Switch item=item_esszimmer_raumfeld_stereo_s_mute label="Lautlos" icon="soundvolume"
Slider item=item_esszimmer_raumfeld_stereo_s_volume label="Lautstärke" icon="soundvolume" switchSupport
Text item=item_esszimmer_raumfeld_stereo_s_volume label="Lautstärke [%s%%]"
Switch item=item_esszimmer_raumfeld_stereo_s_volume label="" icon="soundvolume" mappings=["10"="10%", "20"="20%", "30"="30%", "40"="40%", "50"="50%", "60"="60%"]
}
Frame label="Fitness" {
Switch item=item_fitness_raumfeld_one_m_music label="Musik" icon="soundvolume"
Switch item=item_fitness_raumfeld_one_m_mute label="Lautlos" icon="soundvolume"
Slider item=item_fitness_raumfeld_one_m_volume label="Lautstärke" icon="soundvolume" switchSupport
Text item=item_fitness_raumfeld_one_m_volume label="Lautstärke [%s%%]"
Switch item=item_fitness_raumfeld_one_m_volume label="" icon="soundvolume" mappings=["10"="10%", "20"="20%", "30"="30%", "40"="40%", "50"="50%", "60"="60%"]
}
Frame label="Küche" {
Switch item=item_kueche_raumfeld_stereo_s_music label="Musik" icon="soundvolume"
Switch item=item_kueche_raumfeld_stereo_s_mute label="Lautlos" icon="soundvolume"
Slider item=item_kueche_raumfeld_stereo_s_volume label="Lautstärke" icon="soundvolume" switchSupport
Text item=item_kueche_raumfeld_stereo_s_volume label="Lautstärke [%s%%]"
Switch item=item_kueche_raumfeld_stereo_s_volume label="" icon="soundvolume" mappings=["10"="10%", "20"="20%", "30"="30%", "40"="40%", "50"="50%", "60"="60%"]
}
Frame label="Treppe" {
Switch item=item_treppe_raumfeld_one_s_music label="Musik" icon="soundvolume"
Switch item=item_treppe_raumfeld_one_s_mute label="Lautlos" icon="soundvolume"
Slider item=item_treppe_raumfeld_one_s_volume label="Lautstärke" icon="soundvolume" switchSupport
Text item=item_treppe_raumfeld_one_s_volume label="Lautstärke [%s%%]"
Switch item=item_treppe_raumfeld_one_s_volume label="" icon="soundvolume" mappings=["10"="10%", "20"="20%", "30"="30%", "40"="40%", "50"="50%", "60"="60%"]
}
Frame label="Wohnzimmer" {
Switch item=item_wohnzimmer_raumfeld_stereo_m_music label="Musik" icon="soundvolume"
Switch item=item_wohnzimmer_raumfeld_stereo_m_mute label="Lautlos" icon="soundvolume"
Slider item=item_wohnzimmer_raumfeld_stereo_m_volume label="Lautstärke" icon="soundvolume" switchSupport
Text item=item_wohnzimmer_raumfeld_stereo_m_volume label="Lautstärke [%s%%]"
Switch item=item_wohnzimmer_raumfeld_stereo_m_volume label="" icon="soundvolume" mappings=["10"="10%", "20"="20%", "30"="30%", "40"="40%", "50"="50%", "60"="60%"]
}
}
Während ich diese Raumfeld-Konfiguration für OpenHAB geschrieben habe, ist mir außerdem aufgefallen, dass ich einige Befehle doppelt oder gar mehrfach in meine Rules kopiert habe. Aus diesem Grund habe ich angefangen, komplexere Regeln nochmals in sogenannte Scripts auszulagern. Die Regel, die also meine komplette Wohnung mit Ausnahme meines Büro- und Fitness-Bereiches lautlos stellt, sieht dann nur noch wie folgt aus:
rules/raumfeld_buero.rules
rule "Raumfeld Lautstärke: Nur Büro und Fitness 60%"
when
Item item_all_raumfeld_volume_nur_buero_fitness_60 changed to ON
then
callScript("raumfeld_all_set_volume_nur_buero_fitness_60");
postUpdate(item_all_raumfeld_volume_nur_buero_fitness_60, OFF);
end
Diese Regel ruft letztlich das Script raumfeld_all_set_volume_nur_buero_fitness_60
auf und deaktiviert den Schalter item_all_raumfeld_volume_nur_buero_fitness_60
sofort wieder, da dieser wie ein Taster funktionieren soll. Die eigentliche Arbeit, also das Setzen der richtigen Lautstärken, wird dann von dem Script erledigt:
rules/raumfeld_buero.rules
logWarn("raumfeld", "raumfeld_all_set_volume_nur_buero_fitness_60");
sendHttpGetRequest("http://192.168.0.133:8080/raumserver/controller/setVolume?value=0&id=Bad");
sendHttpGetRequest("http://192.168.0.133:8080/raumserver/controller/setVolume?value=0&id=Bett");
sendHttpGetRequest("http://192.168.0.133:8080/raumserver/controller/setVolume?value=60&id=B%C3%BCro");
sendHttpGetRequest("http://192.168.0.133:8080/raumserver/controller/setVolume?value=0&id=Esszimmer");
sendHttpGetRequest("http://192.168.0.133:8080/raumserver/controller/setVolume?value=60&id=Fitness");
sendHttpGetRequest("http://192.168.0.133:8080/raumserver/controller/setVolume?value=0&id=K%C3%BCche");
sendHttpGetRequest("http://192.168.0.133:8080/raumserver/controller/setVolume?value=0&id=Treppe");
sendHttpGetRequest("http://192.168.0.133:8080/raumserver/controller/setVolume?value=0&id=Wohnzimmer");
Meine aktuelle OpenHAB-Konfiguration für Teufel Raumfeld Boxen kann außerdem hier heruntergeladen werden. Sie beinhaltet meine komplette Konfiguration, muss aber natürlich an die eigenen Boxen, IDs und IP-Adresse angepasst werden. Die Konfiguration sollte in erster Linie als Leitfaden oder Beispiel dienen.