Einbinden Heimkino-Vorstufe HTP1

Einrichtung der openHAB Umgebung und allgemeine Konfigurationsthemen.

Moderatoren: seppy, udo1toni

Antworten
steo
Beiträge: 3
Registriert: 6. Jun 2024 08:07
Answers: 0

Einbinden Heimkino-Vorstufe HTP1

Beitrag von steo »

Hallo,

ich bin komplette neu in OpenHAB und habe mir gerade zum Testen OpenHAB4 installiert.
In den ersten Schritten versuche ich gerade meine Heimkino-Vorstufe "Monoprice HTP1" einzubinden.
Diese bietet ein webinterface zum Steuern. Dazu können die IR-Codes via http gesendet werden:
https://downloads.monoprice.com/files/m ... 210127.pdf
--> Seite 94/95

Im ersten Schritt um das Prinzip zu verstehen versuche ich gerade einfach mal die "Mute"-Funktion mit einem "Switch" abzubilden.
Die Basis-URL ist immer die: "http://192.168.100.252/ircmd?code="
Will man Mute einschalten muss man den IR-Code 4bb4 senden und die URL wird zu "http://192.168.100.252/ircmd?code=4bb4"
Will man Mute ausschalten muss man den IR-Code 4cb3 senden und die URL wird zu "http://192.168.100.252/ircmd?code=4cb3"
Generell wirft das Webinterface immer bei jedem Befehl und auch beim Aufruf der Basis-URL ein json-zurück.
Das sieht so aus (hier mal mit Powerhsell aufgerufen):

Code: Alles auswählen

PS C:\Users\steffen> (Invoke-RestMethod "http://192.168.100.252/ircmd?code=")


volume               : -53
status               : @{raw=; DECSourceProgram=none}
cal                  : @{lipsync=0; vpl=-100; vph=0; ampsense=1,7; diracactive=on; currentdiracslot=0;
                       caltoolconnected=False}
unitname             : HTP-1
bassenhance          : off
eq                   : @{tc=False; treble=; bass=}
upmix                : off
fastStart            : off
fastStartPassThrough : off
night                : off
loudness             : off
dialogEnh            : 0
input                : h1
muted                : True
hw                   : @{fpBright=8}
stat                 : @{TVSoundSrc= ; TVSoundSrcDefault=none; earcLinkStatus=TIMEOUT, mode:EARC; CECStatus=
                       Enable:Off, LastReq:, SysAud:0, Map:0x0000; displayVideoStat=True; displayAudioStat=True;
                       displayAdvancedSettings=True; newupdate=noupdate; gitbranch=master; devrespavail=False;
                       enableSupportTools=False; systemAudio=True; updateprogmsg=}
versions             : @{apm100=258: APM 119 v258, May 20 2023; avController=4.89 Built Aug  3 2021, 14:46:17
                       ; backplane=51; hardware=Backplane HW: 3.  MIO HW: 2  DAC HW: 3; hdmiVer=73.50.34;
                       SerialNumber=305; GuiNodeRed=commit 3071dcc
                       Date:   Mon Jun 5 23:31:49 2023 -0400
                       ; rootfsVer=rootfs-20191215.xz
                       ; swVer=V1.10.0}
videostat            : @{VideoResolution=4k50Hz; VideoColorSpace=RGB; VideoMode=4:4:4; VideoBitDepth=8bpc; HDRstatus=;
                       Video3D=}
peq                  : @{currentpeqslot=13; peqsw=True}
Das heißt der hier interessante Wert der definiert wie gerade der Zustand ist, wäre "Muted" (direkt auf Root-Ebene) der entweder "true" oder "false" ist.

Leider habe ich es noch nicht hinbekommen die Mute-Funktion abzubilden. Mein Code sieht aktuell so aus:

Code: Alles auswählen

UID: http:url:04e25424e6
label: HTP1 HTTP
thingTypeUID: http:url
configuration:
  authMode: BASIC
  ignoreSSLErrors: false
  baseURL: http://192.168.100.252/ircmd?code=
  delay: 0
  stateMethod: GET
  refresh: 30
  commandMethod: GET
  contentType: application/json
  timeout: 3000
  bufferSize: 2048
channels:
  - id: Volume
    channelTypeUID: http:string
    label: Volume
    description: Volume
    configuration:
      mode: READONLY
      stateTransformation: JSONPATH:$.volume
  - id: Mute
    channelTypeUID: http:switch
    label: Mute
    description: null
    configuration:
      onValue: 4bb4
      commandTransformation: MAP:HTP1Mute.map
      offValue: 4cb3
      stateTransformation: JSONPATH:$.muted
      commandExtension: "%2$s"
in dem Openhab ordner habe ich im Unterordner "conf\transform" eine "HTP1Mute.map" mit folgendem Inhalt angelegt:

Code: Alles auswählen

ON=true
OFF=false
true=ON
false=OFF
Könnt ihr mir helfen die Mute-Funktion abzubilden? Ich denke wenn ich diese hin bekomme und das Prinzip verstanden habe bekomme ich die anderen Funktionen auch abgebildet.

Benutzeravatar
udo1toni
Beiträge: 15243
Registriert: 11. Apr 2018 18:05
Answers: 242
Wohnort: Darmstadt

Re: Einbinden Heimkino-Vorstufe HTP1

Beitrag von udo1toni »

Herzlich willkommen im openHAB Forum!

Zunächst einmal: Die Antwort ist (falls sie exakt so aussieht) leider kein JSON. Oder formatiert die Powershell das automatisch um? Dann wäre die raw Ausgabe wichtig (am besten in einem Browser aufrufen und den Quelltext der Antwort kopieren).

Nächster Punkt: True ist etwas anderes als true.
openHAB arbeitet grundsätzlich case sensitive, es gibt ein paar Stellen, an denen das nicht der Fall ist, aber da im überwiegenden Teil jeder einzelne Buchstabe korrekt angegeben werden muss, ist es das einfachste, jederzeit auf exakte Schreibweise zu achten.

Eine commandTransformation benötigst Du hier keinesfalls, denn ein Switch Item sendet entweder ON oder OFF.
Diese beiden Werte werden exakt durch den in onValue bzw. offValue angegebenen Wert ersetzt.
In der commandExtension wird wiederum %2$s mit dem passenden Wert substituiert.

Da als Antwort (falls es sich doch um JSON handelt) ja True oder False geliefert wird, der Switch aber 4bb4 oder 4cb3 erwartet (das sind die beiden als onValue und offValue definierten Werte...), brauchst Du in der stateTransformation eine Verkettung von Transformations.
Im ersten Schritt holt JSONPATH den Boolean Wert (wie gesagt... falls es sich um JSON handelt), im zweiten Schritt wird aus dem Boolean Wert über eine Map Transformation der korrekte Wert gesetzt. Dieser wird schließlich als onValue bzw. offValue auf den Switch Channel geschrieben. Das sieht in der Definition so aus:

Code: Alles auswählen

  - id: Mute
    channelTypeUID: http:switch
    label: Mute
    description: null
    configuration:
      onValue: 4bb4
      offValue: 4cb3
      stateTransformation: JSONPATH:$.muted∩MAP:HTP1Mute.map
      commandExtension: "%2$s"
mit der zugehörigen HTP1Mute.map:

Code: Alles auswählen

True=4bb4
False=4cb3
Das Zeichen ∩ ist nicht auf der normalen Tastatur verfügbar, in der Windows Zeichentabelle heißt es englisch Intersection (korrekt, Schnittmenge), eingedeutscht wurde es allerdings als Durchschnitt (nun ja... mathematische Korrektheit war noch nie die Stärke von Microsoft). Im Hilfetext zur stateTransformation ist das Zeichen aber ebenfalls erwähnt, so dass man es leicht aus dem Hilfetext kopieren kann :)

Mein Tipp an der Stelle: richte parallel einen string Channel ein, den Du zum Testen verwendest. Verlinke diesen mit einem String Item und sende z.B. über die Karaf Konsole die Codes als String (der string Channel kennt kein onValue bzw. offValue).
Lass die stateTransformation dort zunächst weg, um die vollständige Antwort zu Gesicht zu bekommen. Anschließend kannst Du JSONPATH einbauen, im nächsten Schritt das Mapping und erst zum Schluss ziehst Du die funktionierende Transformationskette in den switch Channel.
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet

steo
Beiträge: 3
Registriert: 6. Jun 2024 08:07
Answers: 0

Re: Einbinden Heimkino-Vorstufe HTP1

Beitrag von steo »

vielen dank für das schnelle Feedback.

Es scheint schon "richtiges" json zu sein. Powersehll formatiert es einfach um.
So sieht es im browser aus:

Code: Alles auswählen

{
    "volume": -53,
    "status": {
        "raw": {
            "streamType": 0,
            "inputSampleRateEnum": 0,
            "inputSampleRate": -1,
            "outputSampleRateEnum": 0,
            "outputSampleRate": -1,
            "activity": 0,
            "decProgramFormat": 0,
            "encListeningFormat": 0,
            "delayms": -6,
            "streamInfoBytes": [
                0,
                0,
                0,
                0,
                0
            ]
        },
        "DECSourceProgram": "none"
    },
    "cal": {
        "lipsync": 0,
        "vpl": -100,
        "vph": 0,
        "ampsense": 1.7,
        "diracactive": "on",
        "currentdiracslot": 0,
        "caltoolconnected": false
    },
    "unitname": "HTP-1",
    "bassenhance": "off",
    "eq": {
        "tc": false,
        "treble": {
            "level": 0,
            "freq": 501
        },
        "bass": {
            "level": 0,
            "freq": 100
        }
    },
    "upmix": "off",
    "fastStart": "off",
    "fastStartPassThrough": "off",
    "night": "off",
    "loudness": "off",
    "dialogEnh": 0,
    "input": "h1",
    "muted": true,
    "hw": {
        "fpBright": 8
    },
    "stat": {
        "TVSoundSrc": " ",
        "TVSoundSrcDefault": "none",
        "earcLinkStatus": "TIMEOUT, mode:EARC",
        "CECStatus": " Enable:Off, LastReq:, SysAud:0, Map:0x0000",
        "displayVideoStat": true,
        "displayAudioStat": true,
        "displayAdvancedSettings": true,
        "newupdate": "noupdate",
        "gitbranch": "master",
        "devrespavail": false,
        "enableSupportTools": false,
        "systemAudio": true,
        "updateprogmsg": {
            "updating": false,
            "title": "your title"
        }
    },
    "versions": {
        "apm100": "258: APM 119 v258, May 20 2023",
        "avController": "4.89 Built Aug  3 2021, 14:46:17\n",
        "backplane": "51",
        "hardware": "Backplane HW: 3.  MIO HW: 2  DAC HW: 3",
        "hdmiVer": "73.50.34",
        "SerialNumber": "305",
        "GuiNodeRed": "commit 3071dcc\nDate:   Mon Jun 5 23:31:49 2023 -0400\n",
        "rootfsVer": "rootfs-20191215.xz\n",
        "swVer": "V1.10.0"
    },
    "videostat": {
        "VideoResolution": "4k50Hz",
        "VideoColorSpace": "RGB",
        "VideoMode": "4:4:4",
        "VideoBitDepth": "8bpc",
        "HDRstatus": "",
        "Video3D": ""
    },
    "peq": {
        "currentpeqslot": 13,
        "peqsw": true
    }
}
Hab mal deinen letzten Code eingebunden. Leider funktioniert es noch nicht. Weder das auslesen noch das setzen.
Muss man wenn man die "map-Datei" anpasst openhab neu starten oder so?

Benutzeravatar
udo1toni
Beiträge: 15243
Registriert: 11. Apr 2018 18:05
Answers: 242
Wohnort: Darmstadt

Re: Einbinden Heimkino-Vorstufe HTP1

Beitrag von udo1toni »

steo hat geschrieben: 6. Jun 2024 11:42 s scheint schon "richtiges" json zu sein. Powersehll formatiert es einfach um.
So sieht es im browser aus:
Das passt schon mal besser...

Und dann ist auch true korrekt, nicht True (wie gesagt... Groß/Kleinschreibung)
Aber zunächst mal müssten wir das Senden hinbekommen...
Und wo ich jetzt darüber nachdenke, bin ich mir nicht sicher, dass die Rückmeldung überhaupt jemals ankommen kann (weil der Status über das http Binding aktiv abgefragt wird, mit einer eigenen URL...)
Es scheint mir eher sinnvoll, gleich einen Umweg über eine Rule zu gehen und dieses Gerät nicht über das normale http Binding anzulegen. Leider ist Dein Gerät nicht auf der Liste der kompatiblen Geräte zum Monoprice Binding... Macht aber (fast) nichts, das geht auch "zu Fuß"...

Voraussetzung: Du legst für jede gewünschte Funktion ein passendes Item an, welches dann mittels Rule gefüttert wird. z.B. so:

Code: Alles auswählen

Group gHtp1
Switch htp1_mute "Mute" (gHtp1)
Number htp1_volume "Volume" (gHtp1)
Das Group Item dient als "Sammelbehälter" für alle Items, die zum Gerät gehören.
Nun die passende Rule:

Code: Alles auswählen

rule "htp1"
when
    Member of gHtp1 received command
then
    var String irCode = ""
    switch(triggeringItem.name) {
        case "htp1_mute" : {
            if(receivedCommand == ON)
                irCode = "4bb4"
            else
                irCode = "4cb3"
        }
        case "htp1_volume" : {
            logInfo("htp1","Volume: numerischer Befehl nicht unterstützt!")
        }
        default : return;
    }
    var String json = ""
    if(irCode != "")
        json = sendHttpGetRequest("http://192.168.100.252/ircmd?code="+irCode)
    if(json != "") {
        val mute = if(transform("JSONPATH","$.muted",json)=="true") ON else OFF
        val vol  = transform("JSONPATH","$.volume",json)
        if(htp1_mute.state != mute)
            htp1_mute.postUpdate(mute.toString)
        if(htp1_volume.state != vol)
            htp1_volume.postUpdate(vol)
    }
end
Dieser Code ist eine DSL Rule, die Du in einer Datei im Ordner $OPENHAB_CONF/rules/ ablegen musst. Die Datei muss die Endung .rules haben, sie sollte möglichst unix-konform gespeichert sein (UTF-8, LF als Zeilenende - nicht CRLF)
Alternativ kannst Du die Rule auch über die UI anlegen, dann musst Du allerdings den Trigger der Rule passend setzen (when -> Show All -> a member of an item group receives a command -> Group Item selektieren -> done),
als Action eine DSL Rule wählen (then -> Inline Script -> Rule DSL) und im Codefenster der Rule anschließend den Code einfügen (der Code ist der Teil zwischen den Schlüsselworten then und end, OHNE die beiden Schlüsselworte).

Was macht die Rule? Zunächst löst sie aus, sobald ein Member der Gruppe gHtp1 einen Befehl empfängt (aka Du hast über die UI einen Schalter umgelegt).
Sobald die Rule gestartet wurde, legt sie eine lokale Variable für den zu sendenden IR Code an. Anschließend entscheidet sie anhand des Namens, des Items, welches die Rule ausgelöst hat, welchen IR Code sie senden soll. Findet sie das passende Item nicht, bricht die Rule die Arbeit ab (das ist das return;).
Nachdem der zu sendende Code ermittelt wurde, wird eine lokale Variable für die json Antwort angelegt. Falls irCode nicht leer ist (nur zur Sicherheit...), wird nun der httpRequest abgesetzt. Das Ergebnis landet in der lokalen Variable, welche anschließend ausgewertet wird.
Als Beispiel landet in der lokalen Konstanten mute abhängig davon, ob $.muted true ist oder nicht, der Wert ON oder OFF.
Danach prüft die Rule noch, ob der zugehörige Schalter diesen Zustand hat und setzt ihn, falls das nicht der Fall ist.
Du kannst nun leicht weitere Items ergänzen, sie müssen lediglich Member der Gruppe sein, dann kannst Du ein weiteres case : anlegen und dahinter wie gezeigt unterschiedliche IR Codes zuordnen.
Genauso kannst Du innerhalb des Antwort-Blocks auch eine weitere Auswertung der json Response vornehmen, also weitere lokale Konstanten mit Werten versorgen. Eventuell ist es sinnvoll, die Auswertung zu optimieren, allerdings müsste man dann die Itemnamen geschickt wählen (z.B. gleichlautend mit den Namen der Werte, damit man in einer Schleife über alle Member der Gruppe nacheinander die aktuellen Zustände abfragen kann.
Für den Moment sollte es aber auch "quick 'n' dirty funktionieren.

EDIT: Als Beispiel habe ich noch volume ergänzt... Zusammenhängende Blöcke von Codezeilen werden mit {} markiert (wichtig im Zusammenhang mit bedingten Verzweigungen, if und switch-case) lokale Variablen und Konstanten sind nur im aktuellen Kontext gültig, man muss also aufpassen, wo man eine Variable definiert.
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet

steo
Beiträge: 3
Registriert: 6. Jun 2024 08:07
Answers: 0

Re: Einbinden Heimkino-Vorstufe HTP1

Beitrag von steo »

vielen Dank für den super Support. Ich habe gerade versucht das von dir geschriebene in OpenHAB einzubinden.
Allerdings fehlen mir noch die Zusammenhänge. Ich muss sagen ich bin komplett neu in OpenHAB und es ist mehr oder weniger komplett unkonfiguriert. Mir ist noch nicht ganz klar was ich vorab (vor der Rule) noch wo (things/model/items...etc) wie anlegen muss.
Sagen wir mal ich habe noch gar nichts bzgl. der Vorstufe angelegt. Lediglich das http-binding und json-transformpath installiert, was wären dann die einzelnen Schritte?

Benutzeravatar
udo1toni
Beiträge: 15243
Registriert: 11. Apr 2018 18:05
Answers: 242
Wohnort: Darmstadt

Re: Einbinden Heimkino-Vorstufe HTP1

Beitrag von udo1toni »

Ist ja schon eine Weile her... :)
Wenn man das Gerät ohne Zuhilfenahme des Addons ansteuern will, reichen tatsächlich ein paar Items und die passende Rule.

Nutzt Du openHAB unter Windows, oder läuft es auf einem separaten System?

Die einfachste Variante wäre, z.B. eine Datei htp1.items anzulegen, im Verzeichnis $OPENHAB_CONF/items/, mit dem Inhalt

Code: Alles auswählen

Group gHtp1
Switch htp1_mute "Mute" (gHtp1)
Number htp1_volume "Volume" (gHtp1)
Damit existieren dann unmittelbar diese drei Items.
Anschließend reicht es, eine Datei htp1.rules anzulegen, im Verzeichnis $OPENHAB_CONF/rules/, mit dem Inhalt

Code: Alles auswählen

rule "htp1"
when
    Member of gHtp1 received command
then
    var String irCode = ""
    switch(triggeringItem.name) {
        case "htp1_mute" : {
            if(receivedCommand == ON)
                irCode = "4bb4"
            else
                irCode = "4cb3"
        }
        case "htp1_volume" : {
            logInfo("htp1","Volume: numerischer Befehl nicht unterstützt!")
        }
        default : return;
    }
    var String json = ""
    if(irCode != "")
        json = sendHttpGetRequest("http://192.168.100.252/ircmd?code="+irCode)
    if(json != "") {
        val mute = if(transform("JSONPATH","$.muted",json)=="true") ON else OFF
        val vol  = transform("JSONPATH","$.volume",json)
        if(htp1_mute.state != mute)
            htp1_mute.postUpdate(mute.toString)
        if(htp1_volume.state != vol)
            htp1_volume.postUpdate(vol)
    }
end
Damit existiert dann auch die Rule.
Anschließend musst Du noch mindestens das mute-Item in der Main UI sichtbar machen, um es nutzen zu können.
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet

Antworten