Bravia

An Elixir client for the local REST API on Sony BRAVIA televisions. Control picture settings, power, inputs, and audio over your network, with no cloud service involved.

{:ok, tv} = Bravia.new("192.168.1.20", psk: "0000")
{:ok, 15} = Bravia.Video.get_brightness(tv)
:ok = Bravia.Video.set_brightness(tv, 8)

Installation

def deps do
[{:bravia, "~> 0.1"}]
end

Enabling control on your television

Reading requires no setup. You can query a BRAVIA on your network immediately.

Changing anything requires a Pre-Shared Key, which you set on the television:

Settings → Network → Home Network → IP Control → Authentication → Normal and Pre-Shared Key

Choose a key there and pass it as :psk. Without one, writes fail with %Bravia.Error{reason: :unauthorized} — by far the most common first-run problem.

Finding a television

{:ok, [device | _]} = Bravia.Discovery.discover()
device.friendly_name #=> "SONY XR-42A90K"
device.model #=> "XR-42A90K"
device.host #=> "192.168.1.20"
{:ok, tv} = Bravia.from_device(device, psk: "0000")

Discovery uses SSDP and follows whatever URL the television advertises, which is not a URL you can reliably construct yourself.

What you can control

# Picture
{:ok, targets} = Bravia.Video.get_picture_quality_settings(tv)
:ok = Bravia.Video.set_brightness(tv, 8)
:ok = Bravia.Video.set_picture_mode(tv, :game)
# Power
{:ok, :active} = Bravia.System.get_power_status(tv)
:ok = Bravia.System.set_power_saving_mode(tv, :picture_off)
# Inputs
{:ok, inputs} = Bravia.AvContent.get_current_external_inputs_status(tv)
:ok = Bravia.AvContent.set_play_content(tv, "extInput:hdmi?port=4")
# Audio
{:ok, outputs} = Bravia.Audio.get_volume_information(tv)
:ok = Bravia.Audio.set_volume(tv, 20)
:ok = Bravia.Audio.set_volume(tv, "+1")

Anything without a convenience function is still reachable:

Bravia.RPC.call(tv, "video", "getPictureQualitySettings", [%{"target" => ""}], "1.0")

Things the API will surprise you with

Picture settings are stored per input and per picture mode. A brightness change lands in whichever slot is active and stays there. Nothing puts it back — read the current value first if you intend to restore it.

Ranges are model-specific. This library does not hardcode them. Ask the set:

{:ok, [target]} = Bravia.Video.get_picture_quality_settings(tv, "brightness")
target["candidate"] #=> [%{"min" => 0, "max" => 50, "step" => 1}]

Method versions differ between models. The same television may offer setAudioVolume at both "1.0" and "1.2". Check before assuming:

{:ok, capabilities} = Bravia.capabilities(tv)
capabilities["audio"]["setAudioVolume"] #=> ["1.0", "1.2"]

On OLED sets there is no backlight control.brightness is the panel luminance.

Scope

This is a stateless protocol client. It starts no processes and holds nothing between calls, so polling, caching, and supervision stay in your application where they belong.

Not included: IRCC remote key codes (a separate SOAP endpoint), app launching, and the cookie/PIN pairing flow. Pre-Shared Key authentication only.

Trying it against a real television

bin/bravia drives a set from the command line, so you can check behaviour without writing any Elixir. It finds the television over SSDP, or set BRAVIA_HOST to skip the search. Writes need BRAVIA_PSK.

$ bin/bravia status
power active
saving off
brightness 15
contrast 90
picture game
input HDMI 4 (PC)
volume speaker 0/100
$ bin/bravia inputs
HDMI 1 (Game) extInput:hdmi?port=1
HDMI 2 (Mister) extInput:hdmi?port=2
HDMI 3 (eARC/ARC) (Switch) extInput:hdmi?port=3
* HDMI 4 (PC) extInput:hdmi?port=4
AV extInput:composite?port=1
$ bin/bravia settings brightness
brightness 15 0..50
$ BRAVIA_PSK=0000 bin/bravia brightness 8
ok brightness -> 8

bin/bravia help lists every command, including a call escape hatch for methods with no convenience function. For an interactive session, just console opens IEx with tv bound to a client.

Testing

The suite runs against responses recorded from a real Sony XR-42A90K, so no hardware is needed:

just test # or: mix test

To include the tests that talk to a real television:

BRAVIA_HOST=192.168.1.20 just probe

Add BRAVIA_PSK to include the write test, which changes a picture setting and restores it afterwards. Without a key it is skipped rather than failed.

Re-record the fixtures with BRAVIA_HOST=... just capture.

Development

just lists everything; the ones you will reach for:

RecipeWhat it does
just testoffline suite against fixtures
just probesuite including live hardware tests
just checkfull gate: compile -Werror, format, credo, dialyzer, test
just looksnapshot of the television's current state
just discoverfind Sony televisions on the network
just consoleIEx shell with tv bound
just capturere-record fixtures from a live set
just documentbuild HTML docs
just packagebuild the Hex tarball without publishing

Licence

MIT.