Libratone Zipp (v1) network API
This is an attempt to document the network protocol used to control Libratone Zipp (v1) WiFi speakers, e.g. by the Libratone Android app (com.libratone, version 8.3.1).
The main control protocol is a binary UDP protocol ("LUCI"), not HTTP. HTTP is only used for a status page, UPnP and the initial network setup / firmware update.
API basics
Overview
A Zipp is controlled by sending small UDP datagrams to it. It answers (and sends state changes) via UDP datagrams to the controlling host. All of that happens inside the local network; there is no cloud involved for the basic control.
Ports
Port |
Protocol |
Purpose |
|---|---|---|
7777 (speaker) |
UDP |
Commands to the speaker |
7778 (client) |
UDP |
Replies to commands from the speaker |
3333 (client) |
UDP |
Notifications from the speaker |
3334 (speaker) |
UDP |
Client acknowledges notifications |
1800 (multicast) |
UDP |
Discovery (239.255.255.250:1800) |
80 (speaker) |
TCP |
Web page /cgi?Overview |
49152 (speaker) |
TCP |
UPnP description.xml |
Important points:
Replies are not sent back to the source port of the request. The speaker always sends replies to port 7778 of the requesting IP address, and notifications to port 3333. A client thus has to listen on 7778 and 3333, and can only have one such program per host running at a time unless it uses SO_REUSEADDR/SO_REUSEPORT.
Requests may be sent from any source port.
The speaker needs to know the IP address of the client to be able to send replies. The Android app sends its first command and the speaker remembers the sender, see Registration.
Typical communication
Find the speaker with Discovery (or use a fixed IP).
Send fetch packets to speaker:7777, e.g. for volume, name, play state.
Read the replies on 7778.
Listen on 3333 for notifications, and acknowledge them on speaker:3334.
Send set packets to speaker:7777 to change settings. The speaker confirms the change with a reply on 7778 and/or a notification on 3333.
Android app implementation
com.libratone.v3.luci.LUCIControl sends commands to port 7777 (SendCommand/SendLUCICommand), acknowledgements to port 3334 (SendPacket/SendRespCommand), and runs a ServerThread on 3333. com.libratone.v3.luci.UdpMonitor handles the multicast discovery on 1800.
Unanswered requests are re-sent by the app after a timeout (LUCIControl.manageSentPacket), UDP being unreliable. The random 16 bit value in the packet header (see Packet format) is used to match replies to requests there.
Packet format
All commands, replies and notifications on ports 7777, 7778, 3333 and 3334 use the same packet structure ("LUCI packet", com.libratone.v3.luci.LUCIPacket in the app). All multi-byte numbers are big endian.
Header (10 bytes)
Offset |
Length |
Content |
|---|---|---|
0 |
2 |
Remote ID, always 0xAAAA (43690) |
2 |
1 |
Command type, see below |
3 |
2 |
Command ID, e.g. 0x0040 (64) for volume |
5 |
1 |
Command status, normally 0x00 |
6 |
2 |
"CRC", not a checksum. The app puts a random number between 1 and 65535 here. Implementations use fixed values (0x1234, 0x0000) without problems. |
8 |
2 |
Length of the data section |
10 |
n |
Data |
The data length field is exact; a packet is only valid when len(packet) == 10 + data_length.
The data is almost always printable ASCII or UTF-8 text: numbers are sent as decimal strings ("64", not byte 0x40), flags are the characters "0" and "1", structured data is JSON. Exceptions are documented per command.
Command type
Value |
Name |
Meaning |
|---|---|---|
1 |
fetch |
Request the current value. The data section is empty. |
2 |
set |
Change a value. Also used for replies and notifications and thus can be seen in packets coming from the speaker. |
The app code mentions further values (e.g. 3, 5) in LSSDPNode.fetch()/set(), but these select the transport (USB, Bluetooth, GAIA/BLE for other speaker families), not the UDP packet type.
Command ID
The same ID is used for fetch, set and the reply. There are some exceptions, e.g. the player info (set: 277, fetch: 278) or battery level (fetch: 256, reply: 257). Those are noted at the commands.
Examples
Set volume to 50 % (type=2, command=64, data "50"):
aa aa 02 00 40 00 12 34 00 02 35 30
Fetch the speaker name (type=1, command=90):
aa aa 01 00 5a 00 12 34 00 00
Reply with the name Kitchen:
aa aa 02 00 5a 00 xx xx 00 07 4b 69 74 63 68 65 6e
Note that the Loxone wiki shows the same commands with 00 00 instead of aa aa at the start: the speaker does not seem to check the remote ID.
Common data formats
- bool
The ASCII characters "1" (true) and "0" (false).
- number
Decimal number as ASCII string, e.g. "100". No leading zeros are required, although the Loxone wiki shows two digit values for volumes below 10 ("05"). Both work.
- json
UTF-8 encoded JSON, not newline-terminated.
- voicingId
Identifier of a sound profile, e.g. V100. See Voicing.
- zoneId / linkId
Name of a multi-room group, e.g. LINK ZippMiniOne_xxx (see Discovery).
Replies and notifications carry the same data format as set requests unless noted otherwise.
UDP is unreliable and the speaker may not answer fetch commands it does not support, so clients should use timeouts and retries.
Discovery
Speakers announce themselves with a SSDP-like protocol on the multicast group 239.255.255.250, but on port 1800 instead of the standard 1900.
Search request
Clients send a datagram with the text:
M-SEARCH * HTTP/1.1
to 239.255.255.250:1800. Further headers are not required. (The Android app builds the first line from a version string, HTTP/1.2 or HTTP/1.1, followed by HOST: 239.255.255.250:1800 and PROTOCOL:Version 1.0.)
The reply of the speaker is a NOTIFY message as shown below. The client socket must be bound to port 1800 to see it (libratone-rs binds its search socket to 1800 with SO_REUSEPORT).
Announcements
Speakers also send the same message unsolicited when they boot up, change state (e.g. group membership, name) and from time to time.
NOTIFY * HTTP/1.1 HOST: 239.255.255.250:1800 PROTOCOL: Version 1.0 NTS: ssdp-alive DeviceName: ZIPPTWOGREY_xxx DeviceID: 0123456789ab DeviceState: F,S,P PORT: 7777 ZoneID: LINK ZippMiniOne_xxx Creator: IPAddr: 192.168.178.75 ColorCode: 2010 FWVersion: 1508;4450,;400, StereoPairID: ProVersion: 3.0,3.0 DeviceType: 310 AiMicStatus: 1 Characterstic: 2,100,0,15,7,0,DE,de WifiInfo: ,-59,51/70 ,-62,48/70 VolSync: N
- NTS
Notification sub type:
- ssdp-alive
The speaker is online
- ssdp-byebye
The speaker leaves the network. Clients also send a byebye when they shut down (NTS:ssdp-byebye).
- ssdp-sta_init
Handled by the app (UdpMonitor); meaning not determined.
- DeviceName
Name of the speaker. Contains the serial number after the model name, e.g. Kitchen_9999-H0020000-07-12345.
- DeviceID
12 hex characters, the MAC address of the speaker.
- DeviceState
Comma separated state flags. First letter is the group state:
- F
Free: not part of a multi-room group
- M
Master of a group
- S
Slave in a group
- G / O
Other group-related states handled by the app (GGROUP)
The meaning of the following letters (S, P in the example) is not documented.
- PORT
UDP command port, always 7777.
- ZoneID
Name of the group the speaker belongs to, e.g. LINK Name_xxx. Empty when not in a group.
- Creator
Group creator. Usually empty.
- IPAddr
IP address of the speaker.
- ColorCode
Color of the speaker, see Speaker color.
- FWVersion
Firmware version. First number is the version (e.g. 809, 879), the numbers after the semicolons are further component versions.
- StereoPairID
ID of the stereo pair, empty when not paired.
- ProVersion
Protocol version, e.g. 3.0,3.0.
- DeviceType
Numeric device type. Not present on all firmwares.
- AiMicStatus, Characterstic, WifiInfo, VolSync
Seen on newer speakers (Zipp 2, Zipp Mini 2). WifiInfo contains bssid,rssi,quality/max triples.
- VENDOR
Handled by the app when present.
Notifications
Whenever something changes on the speaker (volume knob turned, track changed, battery level, power state, ...), it sends a packet to port 3333 of the controlling host(s). The packet has the Packet format and carries the command ID and the new data like the answer to a fetch would.
The speaker expects an acknowledgement. Without it, it repeats the notification.
Acknowledgement
Send a packet without data to port 3334 of the speaker:
Python |
command=0, type 2: aa aa 02 00 00 00 xx xx 00 00 |
Rust |
command=2, type copied from the notification |
Both variants are reported to work.
Registration
The speaker sends its notifications to hosts that have recently talked to it. The Android app starts its ServerThread on 3333 and sends commands to port 7777, nothing else is needed.
The libratone-rs code contains a hello packet (set, command 3, data <our ip>,3333) that registers the notification receiver explicitly, but it is not used by the UI. The Python implementation sends a fetch of the charging status (1284) after binding the sockets to "kick" the speaker into sending the notifications.
Notifications seen in practice
Command ID |
Name |
Notes |
|---|---|---|
14 |
Power mode |
|
15 |
Standby timer |
|
51 |
Play status |
|
64 |
Volume |
|
65 |
Firmware update |
|
103 |
Device / group state |
|
278 |
Player info |
|
258 |
Battery level |
|
1284 |
Charging state |
Commands
Playback
Play control (40)
Source: verified
Control playback.
- Command ID
40
- Type
set
- Data
One of the ASCII strings:
PLAY
Start or resume playback
PAUSE
Pause
STOP
Stop (also pauses radio streams)
NEXT
Next track
PREV
Previous track
TOGGL
Toggle play/pause (sic, 5 characters)
MUTE
Mute
UNMUTE
Unmute
Example, PLAY:
aa aa 02 00 28 00 12 34 00 04 50 4c 41 59
When the speaker is in standby, a play command wakes it up (the Loxone wiki uses a favorite preset for that).
Play status (51)
Source: verified
- Command ID
51 (fetch, reply and notification)
- Data in reply
A single ASCII digit:
0
Playing
1
Stopped
2
Paused
3
Next (transient)
4
Previous (transient)
5
Toggle (transient)
6
Muted
7
Unmuted
Volume (64)
Source: verified
- Command ID
64
- Type
fetch, set, reply, notification
- Data
Volume as number between 0 and 100.
The volume is set per speaker, even when speakers are in a multi-room group, unless volume sync is enabled, see Volume sync.
Example, set to 100 %:
aa aa 02 00 40 00 12 34 00 03 31 30 30
Maximum volume (300)
Source: app
- Command ID
300
- Type
fetch, set
- Data
Maximum volume limit as number.
Mute status (520)
Source: verified (fetch)
- Command ID
520
- Type
fetch
- Data in reply
MUTE or UNMUTE.
Use Play control with MUTE/UNMUTE to change it.
Player info (277, 278)
Source: verified
Information about what is currently playing, and the command to start playing something.
- Command ID
278 for fetch, reply and notification
277 for set
- Data
JSON object:
- isFromChannel
bool. true when the current stream was started from a preset
- play_identity
Identifier, e.g. the preset number as string ("1") or the Spotify URI
- play_title
Title or station name
- play_subtitle
Artist or further description
- play_album, play_album_uri, play_artist, play_pic
Metadata when available (picture is an URL)
- play_type
Source type: channel (preset), vtuner, spotify, deezer, tidal, napster, douban, xmly, ...
- play_token
Token for the service, can be empty
- play_username, play_attribution, play_object
Further optional fields
- play_preset_available
1 when the item can be stored as preset
Example reply:
{"isFromChannel":false,"play_identity":"1","play_subtitle":"1","play_title":"channel","play_type":"channel","token":""}
Play info is also used to start playback of preset n (this is what the Loxone template does):
{"isFromChannel":false,"play_identity":"1","play_subtitle":"1","play_title":"channel","play_type":"channel","token":""}
sent with set command 277: the speaker starts the radio station stored as preset 1 and wakes up from standby if needed. Valid play_identity values for the presets are "1" to "5". See also Presets.
Clear play info (298)
Source: app
- Command ID
298
- Type
set with empty data
Play mode (1541)
Source: app
- Command ID
1541
- Type
fetch, set
- Data
String such as repeat/shuffle mode. Used for USB playback.
Play position (USB): 1536, 1537, 1538
Source: app
- 1536
Current USB play ID (fetch, set)
- 1537
USB play mode (fetch, set)
- 1538
USB song info (fetch)
Source selection
Source: app
- 10
Source info (fetch)
- 152
Current source (fetch)
- 121
Switch source (set)
- 123
Available output sources (fetch)
- 128
Current output source (fetch)
- 122
Set output source (set). Values: OUTPUT_WAY_BLUETOOTH (0), OUTPUT_WAY_LOCAL (1), OUTPUT_WAY_AUX (2), OUTPUT_WAY_HDMI (3), OUTPUT_WAY_WIFI_SLAVE (4)
- 1542
Source switch (set)
- 537
Switch to WiFi line-in (set with empty data)
Power
Power mode (14)
Source: verified
- Command ID
14
- Type
fetch, reply, notification
- Data in reply
First byte is an ASCII character:
0
Awake
2
Sleeping (standby)
Standby and standby timer (15)
Source: verified
The same command ID is used to put the speaker to sleep, wake it up, and to configure a timer that puts it to sleep.
- Command ID
15
- Type
set, fetch, notification
- Data
00
Wake up from standby
20
Go to standby immediately ("2" + "0" seconds)
2n
Go to standby after n seconds, e.g. 2900 for 15 minutes, 21800 for 30 minutes
F0
Cancel a running timer
libratone-rs uses 02 to sleep and 00 to wake up. The Python library and the Loxone wiki use 20 and 00.
- Reply to fetch
Binary, not ASCII: byte 0 0x32 ("2") and a little endian 16 bit number of seconds in bytes 1 and 2 when a timer is set. 0xFF as first byte (or no data) means no timer is set. This is the configured duration, not the remaining time.
Example, go to standby in 15 minutes:
aa aa 02 00 0f 00 12 34 00 04 32 39 30 30
The app also calls the command setPowerMode (data "0") to wake the speaker up and setOffTime.
Sound and voicing
Voicing (516, 518, 524)
Source: verified
A "voicing" is an equalizer profile ("Tone" in the Loxone wiki).
- Command ID
516 fetch current voicing
518 set voicing
524 fetch list of all voicings
- Data
voicingId for 516 and 518, JSON list for 524.
Voicings known from the app (assets voicings/voicings.json):
ID |
Name |
Description |
|---|---|---|
V100 |
Neutral |
Basic neutral setting |
V101 |
Easy Listening |
Easy and smooth, leaned back sound |
V102 |
Soft and Comfortable |
Soft midrange for compressed recordings |
V103 |
Rock the house |
Extra drum kick, smooth midrange |
V104 |
Jazz club |
Open acoustic sound, focus on voices |
V105 |
Movie time |
Extra action and movie re-equalization |
V106 |
Live concert |
When the music is loud and dynamic |
V107 |
Classical |
Enjoy grand pianos when they are best |
V108 |
Speech |
For TV programs with subtle voices |
Reply to 524:
[{"description":"Basic neutral setting","name":"Neutral","voicingId":"V100"}, ...]
Example, set "Neutral":
aa aa 02 02 06 00 12 34 00 04 56 31 30 30
Room correction / FullRoom (517, 519, 525)
Source: verified
FullRoom(tm) adapts the sound to the position of the speaker (corner, wall, free-standing, ...).
- Command ID
517 fetch current room setting
519 set room setting
525 fetch list of all room settings
- Data
Room ID like neutral or outdoor (lower case) or similar, and JSON list for 525:
[{"description":"Basic neutral setting","name":"Neutral","voicingId":"neutral"}, ...]
Known IDs from the Loxone wiki: neutral, outdoor. Always fetch 525 to get the full list for your speaker.
- 545
Room correction on/off (fetch, set)
Volume sync (506)
Source: app
- Command ID
506
- Type
fetch, set
- Data
bool. When 1, the volume of all speakers in a group is changed together.
Stereo type (512, 515)
Source: app
- 512
Set stereo type (set), for stereo pairs
- 515
Fetch speaker stereo type
Presets (favorites)
The speaker has five preset buttons that store a radio station or a streaming service item.
Fetch presets (275)
Source: verified
- Command ID
275
- Type
fetch
- Data in reply
JSON list of channel objects:
[ { "channel_id": 1, "channel_identity": "31375", "channel_name": "Radio Meuh", "channel_type": "vtuner", "isPlaying": false, "play_token": "" } ]- channel_id
Preset slot, 1 to 5
- channel_type
vtuner, xmly, doubanfm, spotify, kaishu, deezer, tidal, napster
- channel_identity
Station/item ID at the service
- channel_name
Display name
- station_url, picture_url, username, password, play_token
Optional fields
Store a preset (276)
Source: verified (libratone-rs, app)
- Command ID
276
- Type
set
- Data
One channel object as shown above, JSON encoded.
Play a preset
Use set on command 277 with the preset slot, see Player info.
Multi-room
Speakers can be linked in the app by drag and drop. All speakers of such a "link" (also called zone, group or SoundSpace Link) play the same stream. The group membership is stored on the speakers. The group is identified by a ZoneID like LINK ZippMiniOne_xxx.
Behavior:
A stream change (preset, play, pause) on any speaker is mirrored to the other speakers.
The volume stays per speaker (unless Volume sync is on).
Switching a speaker off does not remove it from the group. When switched on again it re-joins and starts the stream.
Device and group state (103)
Source: verified (notification)
- Command ID
103
- Type
fetch, notification
- Data
ASCII text, e.g. MASTER,LINK <link id>, SLAVE,LINK <link id>, GROUPED,LINK <link id>. Strings containing UNGROUP or UNLINK mean the speaker left the group. Some captures show a leading : or = that should be stripped.
Join group (502)
Source: verified
- Command ID
502 (0x01F6)
- Type
set
- Data
LINK <link id>
Leave group (503)
Source: verified
- Command ID
503 (0x01F7)
- Type
set
- Data
LINK <link id>
Join group as slave (1793)
Source: app
- Command ID
1793
- Type
set
- Data
Zone ID. Used for the "slave only" join of newer firmware.
Device info
Firmware version (5)
Source: verified
- Command ID
5
- Type
fetch
- Data in reply
Firmware version string.
Speaker name (90)
Source: verified
- Command ID
90
- Type
fetch, set
- Data
UTF-8 name of the speaker, e.g. Kitchen.
Setting the name also changes the DeviceName in the Discovery announcements. (The app uses command 816 in addition for some speaker families.)
Serial number (769)
Source: verified
- Command ID
769
- Type
fetch
- Data in reply
Serial number, e.g. 1234-A1234567-12-12345.
Speaker color (770, 771)
Source: verified
- Command ID
770 fetch, 771 set
- Data
Four digit color code. Known codes from the app:
1001
Pepper Black
1002
Salty Grey
1003
Steel Blue
1004
Raspberry Red
1005
Almond Brown
1006
Icy Blue
1007
Pineapple Yellow
1008
Petrol Blue
1009
Plum Purple
2001
Cloudy Grey
2002
Graphite Grey
2003
Victory Red
2004
Deep Lagoon
2005
Sangria
2006
Atlantic Deep
2007
Signal
2008
Nude
2009
Pastel Blue
9000
Black
9001
White
(Some codes have _OLD variants with different shades in the app.)
Battery level (256, 257, 258)
Source: verified
- Command ID
256 fetch
257 reply
258 notification
The reply to the fetch for 256 may come with command ID 256 and empty data first; the real value follows as 257.
- Data
Battery level in percent as number, e.g. 59.
Charging state (1284)
Source: verified
- Command ID
1284
- Type
fetch, reply, notification
- Data
0
Discharging (running on battery)
1
Plugged in, charging
2
Plugged in, fully charged
3
Plugged in, not charging
Signal strength (529)
Source: verified
- Command ID
529
- Type
fetch
- Data in reply
Example -86,-42,5/5: noise and signal level in dBm, and quality as current/max.
Capabilities (281)
Source: verified (libratone-rs)
- Command ID
281
- Type
fetch
- Data in reply
JSON:
{"capabilities":[{"name":"vtuner"},{"name":"spotify"}, ...]}List of streaming services supported by the speaker. (fetchMusicServiceCapability in the app.)
Limited function list (304)
Source: app
- Command ID
304
- Type
fetch
- Data in reply
Three bytes, meaning unknown.
Firmware update (65)
Source: verified (libratone-rs), app
- Command ID
65
- Type
set, notification
- Data
The app builds an UpdateFirmwareCommand with an action number (checks update status 0, checks update version 2, downloads new version 3, starts update 4). The speaker reports its progress as notifications.
- 530
OTA auto download: AUTO or MANUAL (fetch, set)
Wi-Fi credentials
Source: app
The app has a setWiFiCredentials method that sends the network configuration with a command; the command ID and format have not been analyzed. See Setup mode for the HTTP based way used by the setup mode.
LED
LED always on (613)
Source: app, Loxone wiki
- Command ID
613
- Type
fetch, set
- Data
bool. 0 = LED always on, 1 = LED turns off a few seconds after the last interaction (and lights up briefly on button presses).
Loxone wiki example, "always on":
00 00 02 02 65 00 00 00 00 01 30
LED brightness (301)
Source: app, Loxone wiki
- Command ID
301
- Type
fetch, set
- Data
Brightness in percent, 0 to 100.
LED level (296)
Source: app
- Command ID
296
- Type
fetch, set
- Data
LED display level as string (older firmware).
Voice prompt
Source: app
- 293
Voice prompt on/off (bool)
- 294
Voice prompt language
- 299
List of supported voice prompt languages (fetch)
Other commands
Command IDs found in the Android app (LSSDPNode) that are not covered by the other implementations. Whether they work on a Zipp 1 is unknown; most are meant for newer speakers or headphones.
Names are the app method names. Type F is a fetch, S a set request.
Command ID |
Type |
Meaning |
|---|---|---|
66 |
F, S |
Delta N firmware update status / start update |
274 |
S |
Secret |
283 |
S |
Smart EQ |
289 |
F, S |
Device user info (fetchDeviceUserInfo) / gesture behavior |
290 |
S |
Set current user as owner |
291 |
S |
Remove user info |
297 |
S |
Update user info |
787 |
F |
Alexa (AVS) notification |
788 |
F, S |
Alexa (AVS) language |
1025 |
F |
Alexa (AVS) voice recognition status |
1031 |
F, S |
AI microphone status (AiMicStatus) |
1285 |
F, S |
Private mode |
2048 |
S |
Alarm operation |
2304 |
F |
AI service capability |
2305 |
F |
AI service status |
2306 |
S |
AI service metadata |
2312 |
S |
Alexa auth data |
860, 861 |
F, S |
Trial status |
4102 to 4108 |
F, S |
Bluetooth device discovery, connect, disconnect, unpair |
Newer speakers with a negative ID (e.g. -250 for ANC level, -248 ANC status, -255/-254 LED always on) exist too, and are listed in the app's LSSDPNode class.
HTTP
HTTP services
HTTP is not used for playback control. The following HTTP endpoints exist.
Status page
Source: Loxone wiki
- GET http://<speaker>/cgi?Overview
HTML page with status information of the speaker.
UPnP description
Source: Loxone wiki
- GET http://<speaker>:49152/description.xml
Standard UPnP device description (XML).
Other /cgi? URLs
The app also contains the strings /cgi?GetList, /cgi?PlayList, /cgi?ResetPlayer, /cgi?Update and /cgi?UploadFile. Their parameters and behavior are unknown.
Setup mode and firmware flashing
Source: app (com.libratone.v3.net.WebPageOperations)
A speaker that is not configured, or is in setup mode, opens its own access point. The app (and UdpMonitor) then talks to the speaker at 192.168.6.1. The web server uses goform handlers.
- GET http://<speaker>/index.asp
Returns a page with a JavaScript variable aspgetstringval, which contains the list of Wi-Fi networks seen by the speaker and the current settings.
- POST http://<speaker>/goform/formHandlerConfigureProfileSettingsAndDmpName
Configure the Wi-Fi network and/or the speaker name. Content-Type: application/x-www-form-urlencoded.
- DMPName
Speaker name
- ProfileName
Profile1
- SSID
Network name
- DHCPClient
DHCP enabled flag
- Passphrase
WPA/WPA2 password
- KeyIndex, Key0 to Key3
WEP keys
The security type is sent as a text value of the form (NONE), (WEP), (WPA ...) or (WPA2 ...); the name of the form field has not been determined.
When only the name is changed, only DMPName is sent.
- GET http://<speaker>/bl_index.asp
Bootloader page, used to update the firmware of old speakers (Live, Lounge).
- POST http://<speaker>/goform/formPostHandler
Multipart upload of firmware, field appFirmware, file name apsw.bcd.
- POST http://<speaker>/goform/formDownloadConfirmationHandler?button=++++++Ok++++++
Confirm the firmware upload.
- GET http://<speaker>/1000/firmware_update/bl_flash_burn_status.asp
Flash progress.
Examples
Examples
Python
A minimal client that sets the volume and reads the speaker name:
import socket, struct
SPEAKER = '192.168.178.75'
def packet(cmd, data=b'', typ=2):
return struct.pack('>HBHBHH', 0xaaaa, typ, cmd, 0, 0x1234, len(data)) + data
# bind first: replies always go to port 7778
rx = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
rx.bind(('', 7778))
rx.settimeout(2)
tx = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
tx.sendto(packet(64, b'30'), (SPEAKER, 7777)) # set volume 30
tx.sendto(packet(90, typ=1), (SPEAKER, 7777)) # fetch name
data, addr = rx.recvfrom(4096)
cmd = struct.unpack('>H', data[3:5])[0]
payload = data[10:]
print(addr, cmd, payload)
Shell
Using socat:
# play printf '\xaa\xaa\x02\x00\x28\x00\x12\x34\x00\x04PLAY' | socat - UDP:192.168.178.75:7777 # discovery printf 'M-SEARCH * HTTP/1.1' | socat - UDP4-DATAGRAM:239.255.255.250:1800,so-broadcast
About
Sources of this documentation:
https://www.loxwiki.eu/display/LOX/Libratone+Zipp+WLan+Lautsprecher (Benjamin Hanke)
python_libratone_zipp by Chouffy: https://github.com/Chouffy/python_libratone_zipp
libratone-rs, a Rust implementation
Static analysis of the Libratone Android app 8.3.1 (classes com.libratone.v3.luci.LUCIPacket, com.libratone.v3.luci.LUCIControl, com.libratone.v3.luci.UdpMonitor, com.libratone.v3.model.LSSDPNode, com.libratone.v3.net.WebPageOperations)
Each command lists where it is known from:
- verified
Used by at least one of the independent implementations, which have been tested against real Zipp 1 speakers (firmware 809, 879).
- app
Only seen in the Android app code. The command ID and data format are taken from the app, but it has not been confirmed on a Zipp 1.
The app supports many more speaker families (Air, Citi, Gen, ...) that speak the same protocol. Not every command makes sense on a Zipp.
Last update: 2026-11-12T17:17:15+01:00
License
It is licensed under the GNU Free Documentation License.
Building
You need to install rst2html5 before:
$ pip install rst2html5-tools
or on Debian:
$ apt install docutils-common
Rendering the docs is done via a build script:
$ ./build.sh