I’m looking for an integration for my Harman Kardon Citation MultiBeam™ as well. I haven’t found yet so I reversed engineered the local API a bit. As for source selection I think you have to look in to your TV because the HK 700 only has one input source to my understanding (one HDMI in).
However, for future reference. If someone wants to turn this into a full HA Add-On for HK 700, please feel free to do so.
For now, I added the following to my HA configuration.yaml using the REST integration:
sensor:
# Harman Kardon Citation Multibeam 700
# - http://100.127.0.25:4323/api/getData?path=citation%3AsummitInfo&roles=value
# - http://100.127.0.25:4323/api/getData?path=citation%3AsubLineOutState&roles=value
# - http://100.127.0.25:4323/api/getData?path=settings%3A%2Fsound%2FsubwooferCrossover&roles=value
# - http://100.127.0.25:4323/api/getData?path=settings%3A%2Fsound%2Fsurround%2FbassTrimLevel&roles=value
# - http://100.127.0.25:4323/api/getData?path=settings%3A%2Fsound%2Fsurround%2FtrebleTrimLevel&roles=value
# - http://100.127.0.25:4323/api/getData?path=settings%3A%2Fcitation%2FlineOutputModeFixed&roles=value
# - http://100.127.0.25:4323/api/getData?path=settings%3A%2Fcitation%2FauxInTrimState&roles=value
# - http://100.127.0.25:4323/api/getData?path=settings%3A%2Fsound%2Fsurround%2FleftRear&roles=value
# - http://100.127.0.25:4323/api/getData?path=settings%3A%2Fsound%2Fsurround%2FrightRear&roles=value
# - http://100.127.0.25:4323/api/getData?path=citation%3AsummitInfo&roles=value
# - http://100.127.0.25:4323/api/getData?path=settings%3A%2FmediaPlayer%2FmaxVolume&roles=value
# - http://100.127.0.25:4323/api/getData?path=settings%3A%2Fsound%2FdualMonoMode&roles=value
# - http://100.127.0.25:4323/api/getData?path=settings%3A%2Fsound%2FlipsyncDelay&roles=value
# - http://100.127.0.25:4323/api/getData?path=settings%3A%2Fsound%2FsubwooferFullRangeMode&roles=value
# - http://100.127.0.25:4323/api/getData?path=settings%3A%2Fairplay%2FaddedToHome&roles=value
# - http://100.127.0.25:4323/api/getData?path=settings%3A%2FdeviceName&roles=value
# - http://100.127.0.25:4323/api/getData?path=airplay%3AstarredPassword&roles=value
# - http://100.127.0.25:4323/api/getData?path=settings%3A%2Fairplay%2FdeviceName&roles=value
# - http://100.127.0.25:4323/api/getData?path=network%3Aprofile&roles=value
# auxInTrimState
- platform: rest
name: HK700_auxInTrimState
resource: http://192.168.4.210/api/getData?path=settings%3A%2Fcitation%2FauxInTrimState&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "{{ value_json[0].auxInTrimState }}"
scan_interval: 120
# subwooferCrossover
- platform: rest
name: HK700_subwooferCrossover
resource: http://192.168.4.210/api/getData?path=settings%3A%2Fsound%2FsubwooferCrossover&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "{{ value_json[0].i32_ }}"
scan_interval: 120
# leftRear speaker trim level
- platform: rest
name: HK700_leftRearTrim
resource: http://192.168.4.210/api/getData?path=settings%3A%2Fsound%2Fsurround%2FleftRear&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "{{ value_json[0].surroundSpeaker.speakerTrimLevel }}"
json_attributes:
- surroundSpeaker
scan_interval: 120
# rightRear speaker trim level
- platform: rest
name: HK700_rightRearTrim
resource: http://192.168.4.210/api/getData?path=settings%3A%2Fsound%2Fsurround%2FrightRear&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "{{ value_json[0].surroundSpeaker.speakerTrimLevel }}"
json_attributes:
- surroundSpeaker
scan_interval: 120
# trebleTrimLevel
- platform: rest
name: HK700_trebleTrimLevel
resource: http://192.168.4.210/api/getData?path=settings%3A%2Fsound%2FtrebleTrimLevel&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "{{ value_json[0].i32_ }}"
scan_interval: 120
# bassTrimLevel
- platform: rest
name: HK700_bassTrimLevel
resource: http://192.168.4.210/api/getData?path=settings%3A%2Fsound%2FbassTrimLevel&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "{{ value_json[0].i32_ }}"
scan_interval: 120
# lipsyncDelay
- platform: rest
name: HK700_lipsyncDelay
resource: http://192.168.4.210/api/getData?path=settings%3A%2Fsound%2FlipsyncDelay&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "{{ value_json[0].i32_ }}"
scan_interval: 120
# maxVolume
- platform: rest
name: HK700_maxVolume
resource: http://192.168.4.210/api/getData?path=settings%3A%2Fsound%2FmaxVolume&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "{{ value_json[0].i32_ }}"
scan_interval: 120
# lineOutputModeFixed
- platform: rest
name: HK700_lineOutputModeFixed
resource: http://192.168.4.210/api/getData?path=settings%3A%2Fcitation%2FlineOutputModeFixed&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "{{ value_json[0].bool_ }}"
scan_interval: 120
# subwooferFullRangeMode
- platform: rest
name: HK700_subwooferFullRangeMode
resource: http://192.168.4.210/api/getData?path=settings%3A%2Fsound%2FsubwooferFullRangeMode&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "{{ value_json[0].bool_ }}"
scan_interval: 120
# dualMonoMode
- platform: rest
name: HK700_dualMonoMode
resource: http://192.168.4.210/api/getData?path=settings%3A%2Fsound%2FdualMonoMode&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "{{ value_json[0].bool_ }}"
scan_interval: 120
# subLineOutState
- platform: rest
name: HK700_subLineOutState
resource: http://192.168.4.210/api/getData?path=citation%3AsubLineOutState&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "{{ value_json[0].bool_ }}"
scan_interval: 120
# summitInfo
- platform: rest
name: HK700_summitInfo
resource: http://192.168.4.210/api/getData?path=citation%3AsummitInfo&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "OK"
json_attributes_path: "$[0].summitInfo"
json_attributes:
- txPoweredOn
- tx
- rxSlaves
- networkRunning
scan_interval: 120
# airplayAddedToHome
- platform: rest
name: HK700_airplayAddedToHome
resource: http://192.168.4.210/api/getData?path=settings%3A%2Fairplay%2FaddedToHome&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "{{ value_json[0].bool_ }}"
scan_interval: 120
# deviceName
- platform: rest
name: HK700_deviceName
resource: http://192.168.4.210/api/getData?path=settings%3A%2FdeviceName&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "{{ value_json[0].string_ }}"
scan_interval: 120
# airplayStarredPassword
- platform: rest
name: HK700_airplayStarredPassword
resource: http://192.168.4.210/api/getData?path=airplay%3AstarredPassword&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "{{ value_json[0].string_ }}"
scan_interval: 120
# airplayDeviceName
- platform: rest
name: HK700_airplayDeviceName
resource: http://192.168.4.210/api/getData?path=settings%3A%2Fairplay%2FdeviceName&roles=value
method: GET
headers:
Content-Type: application/json
value_template: "{{ value_json[0].string_ }}"
scan_interval: 120
Then reboot. Just edit the IP address for each resource: http://...
The first lines are just comments for reference with all possible GET calls I found to gather information out of the HK 700, ignore port 4323. It runs on 80 by default, this is my internal port forwarding.
Sending changes back should be possible too. It seems to call in a similar format API endpoints /api/setData
instead of /api/getData
but i didn’t work that out for HA yet.
This information was gathered from the Web GUI that uses this api on the local IP address with paths /webclientAmp/
and /settings.fcgi
and /network.fcgi
. Path /googlecast.fcgi
does not seem to contain any data. Path /main.fcgi
contains a JSON object in HTML with information instead of making a REST call. I fetched that using the Node-Red and MQTT (Add-On and integration) by making the following Node-Red flow:
[{"id":"984c3ccbfbe6728b","type":"tab","label":"HK700 FetchMain","disabled":false,"info":"","env":[]},{"id":"217d4adf253aafd4","type":"http request","z":"984c3ccbfbe6728b","name":"Fetch main.fcgi","method":"GET","ret":"txt","paytoqs":"ignore","url":"http://192.168.4.210/main.fcgi","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":320,"y":120,"wires":[["b9e6a3476e1bf63b"]]},{"id":"b9e6a3476e1bf63b","type":"html","z":"984c3ccbfbe6728b","name":"Get SCRIPT tags from HTML","property":"payload","outproperty":"payload","tag":"head > script","ret":"html","as":"single","chr":"_","x":540,"y":120,"wires":[["23dd3d8b3f56ac05"]]},{"id":"23dd3d8b3f56ac05","type":"function","z":"984c3ccbfbe6728b","name":"Extract JSON directly from the SCRIPT content","func":"// Join lines and filter empty\nlet lines = msg.payload.filter(line => line.trim() !== '');\nlet input = lines.join('\\n');\n\nif (typeof input !== 'string') {\n node.warn(\"Payload after join is not string\");\n return null;\n}\n\n// Extract JSON string inside JSON.parse()\nlet regex = /var profile = JSON\\.parse\\('(.+?)'\\)/s;\nlet match = input.match(regex);\n\nif (!match) {\n node.warn(\"No JSON.parse(...) match found\");\n return null;\n}\n\nlet escapedJson = match[1];\n\n// Unescape HTML entities\nfunction htmlUnescape(str) {\n return str.replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>');\n}\nlet unescapedHtml = htmlUnescape(escapedJson);\n\n// Unescape backslashes, e.g., \\\\\" -> \", \\\\ -> \\\nlet unescapedBackslashes = unescapedHtml.replace(/\\\\\\\\/g, '\\\\').replace(/\\\\\"/g, '\"');\n\ntry {\n let profile = JSON.parse(unescapedBackslashes);\n msg.payload = profile;\n return msg;\n} catch(err) {\n node.error(\"JSON parsing failed: \" + err.message);\n return null;\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":860,"y":120,"wires":[["cf842fa238a8255b"]]},{"id":"cf842fa238a8255b","type":"mqtt out","z":"984c3ccbfbe6728b","name":"","topic":"HK700/main","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"b825456db90257d5","x":1150,"y":120,"wires":[]},{"id":"6507d70170be1967","type":"comment","z":"984c3ccbfbe6728b","name":"HA-MQTT note","info":"Home Assistant integration does not\nwork properly due to dependency issues.\n\nUsing MQTT to integrate instead.\n","x":160,"y":60,"wires":[]},{"id":"b2b1ca08f4e0a649","type":"mqtt in","z":"984c3ccbfbe6728b","name":"","topic":"HK700/start","qos":"2","datatype":"auto-detect","broker":"b825456db90257d5","nl":false,"rap":true,"rh":0,"inputs":0,"x":150,"y":120,"wires":[["217d4adf253aafd4"]]},{"id":"b825456db90257d5","type":"mqtt-broker","name":"Mosquitto broker in Home Assistant","broker":"localhost","port":1883,"clientid":"","autoConnect":true,"usetls":false,"protocolVersion":4,"keepalive":60,"cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closeRetain":"false","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""}]
And setting up the following Automation in Home Assistant to pull it every 120 seconds:
alias: Pull HK700 main data through Node-Red
description: ""
triggers:
- trigger: time_pattern
minutes: /2
conditions: []
actions:
- action: mqtt.publish
metadata: {}
data:
evaluate_payload: false
qos: "2"
retain: false
topic: HK700/start
mode: single
And the following MQTT sensors in HA configuration.yaml to store them into sensors.
mqtt:
sensor:
- name: "HK700 WiFi SSID"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.wireless.ssid }}"
- name: "HK700 WiFi Signal Level"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.wireless.signalLevel }}"
unit_of_measurement: "dBm"
- name: "HK700 WiFi Frequency"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.wireless.frequency }}"
unit_of_measurement: "MHz"
- name: "HK700 WiFi Channel"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.wireless.channel }}"
- name: "HK700 WiFi BSSID"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.wireless.bssid }}"
- name: "HK700 WiFi MAC"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.wireless.mac }}"
- name: "HK700 WiFi State"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.wireless.state }}"
- name: "HK700 WiFi Connecting Status"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.wireless.connectingStatus }}"
- name: "HK700 WiFi Encryption"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.wireless.encryption }}"
- name: "HK700 WiFi Credentials Suspect"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.wireless.credentialsSuspect }}"
- name: "HK700 WiFi IP"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.wireless.addresses[0].ip }}"
- name: "HK700 WiFi DHCP"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.wireless.addresses[0].dhcp }}"
- name: "HK700 Gateway IP"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.gateways[0].ip }}"
- name: "HK700 DNS IP"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.dns[0].ip }}"
- name: "HK700 SoftAP State"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.softAp.state }}"
- name: "HK700 Wired Cable Connected"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.wired.cableConnected }}"
- name: "HK700 Wired State"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.wired.state }}"
- name: "HK700 Wired Connecting Status"
state_topic: "HK700/main"
value_template: "{{ value_json.networkInfo.wired.connectingStatus }}"
I expect this to work for other HK Citation MultiBeam models too, but obviously I didn’t test that.