I just got my speakers and didn’t want to use the remote to change the volume and the official kef integration wasn’t working for me so I made a script myself. I tried a bunch of searching and couldn’t find a complete solution. I figured I’d post this so someone else can build off of it.
First step is to add the restful API for your speakers to home assistant. You’ll need to make a DHCP reservation for your speakers and add this line to your configuration.yaml (replace 192.168.1.78 with your reservation IP - you may need to reboot your speakers if you chose a different IP than the one it currently has.)
rest_command:
kef_office:
url: "http://192.168.1.78/api/{{cmd}}"
method: GET
verify_ssl: false
After this, you need to restart home assistant.
Once it’s back up and running, you can test this works by going to home assistant->developer tools → actions. For the action, pick the name you put above (‘kef_office’) as the action and use this for Action data:
cmd: getData?path=player:volume&roles=value
Press “Perform Action” and you should get output like:
content:
- type: i32_
i32_: 80
status: 200
headers:
Date: Thu, 27 Nov 2025 03:17:16 GMT
X-Robots-Tag: noindex
Transfer-Encoding: chunked
Content-Type: application/json
The 80 above is the current volume. Now we can write a script that increases or decreases the volume from that value.
I added an “input number” helper named kef_office_cur_vol that holds the current volume and created a new script (named kef_office - set volume) with the below yaml:
sequence:
- action: rest_command.kef_office
response_variable: getvol_response
data:
content-type: application/json
cmd: getData?path=player:volume&roles=value
- action: input_number.set_value
metadata: {}
data:
value: >-
{{ ((getvol_response.content | string).rsplit(":",1)[-1].split('}')[0] |
int)+rotate_amount }}
target:
entity_id: input_number.kef_office_cur_vol
- action: rest_command.kef_office
data:
cmd: >-
setData?path=player:volume&roles=value&value={"type":"i32_","i32_":
{{states.input_number.kef_office_cur_vol.state}} }
alias: kef_office - set volume
description: ""
fields:
rotate_amount:
selector:
text: null
name: rotate_amount
required: true
It takes one number parameter (named rotate_amount) that represents a signed integer for how much to change the volume. I use my stream deck to supply the rotate_amount.
There are three steps to the script:
- Like the first test, we grab the the current volume using the restful command and store the response in the var named ‘getvol_reseponse’. This will hold the ‘content’ section in the test response above.
- We then parse the output by grabbing the substring after the last ‘:’ (i.e. the 80), add the input parameter value (rotate_amount) and assign that to the input helper you created earlier (mine was kef_office_cur_vol).
- Finally, we make another restful API request, but this time we set the volume to the new amount. This cmd string is slightly different - it uses setData instead of getData and the ‘value’ we supply is a json string with the actual values.
You can hook this script up to a button or control you have, or run automations like decrease the volume when you leave the room, etc. Like I said earlier, I hooked it up to my stream deck so I can use the knobs on it to control the volume.
The operation isn’t immediate - I’d say there’s around a 0.5 to 1s latency between turning the dial and the volume changing.
There are APIs to control other aspects of the speakers, you can look at pykefcontrol/pykefcontrol/kef_connector.py at 3769677b0e2d74de3235f35a7e49f8bfee421756 · N0ciple/pykefcontrol · GitHub to hookup additional functionality.