|====================================| | ArtiTouchAPI Protocol Version 1.12 | | Written by: Michel van Osenbruggen | | CopyRight 2026 ArtiLED B.V. | |====================================| Latest change : 18-10-2026 ======================= | Protocol Definition | ======================= Protocol : HTTP Port : 80 URL : http://touch_ip/api DATA : GET or POST variables "token" and "data" (JSON object, {} when empty) Token : The Touch's own token (data/token) |=============| | Error Codes | |=============| 1 : Login Failed (invalid username or password) 2 : Token Invalid (request new token) 3 : Incomplete data (not all data supplied) 4 : Invalid data (data value incorrect) 5 : Invalid item (the visualizer, meter, wave, fluid, saver, clock or audio input does not exist) 7 : Command Execution Failed |===================| | Command Structure | |===================| $ response : Command Response => { "success":1/0, "error":error Number, "error_text":"Error Text", "execution_time":ms, "data": { data } } ================== | Data Structure | ================== $ name : {"name":"name"} -> Touch Name $ ident : {"ident":"ident"} -> Touch Ident $ version : {"version":"version"} -> Touch Software Version $ token : {"token":"token"} -> Touch Token $ show : "nowplaying" | "queue" | "info" | "lyrics" | "weather" | "home" : nowplaying/queue/info/lyrics/weather = that page, home = the main menu : lyrics = the words of the song that plays; "Sorry no Lyrics" when it has none, and never words for a film, a series or a YouTube video : (CHANGED in 1.02: visualizer, saver and clock have their own commands /visualizer, /saver, /clock; on and off are /panel) : (CHANGED in 1.03: the VU and digital meters are no visualizers any more - they are the Meters, with their own commands /meters and /meter and their own show "meters" in /running; their ids stay the vu_* they had. /visualizer with a vu_* id now answers error 5, and /visualizers no longer lists them) : (ADDED in 1.04: lyrics) : (ADDED in 1.05: the Waves - the real signal of a Local input drawn as lines - with their own commands /waves and /wave and their own show "waves" in /running; the ids waveform and waveform_m are no visualizers any more: /visualizer with them answers error 5. The visualizer with the id waves is named Swell; its id stays waves) : (ADDED in 1.06: the Fluids - liquid surfaces moved by the music of a Local input - with their own commands /fluids and /fluid and their own show "fluids" in /running) $ input : {"id":N,"name":"Input N","level":-12..12,"sound":1|0|null} -> one of the Touch's own audio inputs that is there (a grabber plugged in); level = its installer's Input level in whole dB (0 = unchanged; it changes what the meters and visualizers see, not the loudness); sound = sound on it now (null = not known). The id (1, "1") or the name ("Input 1", any case) is what /input_level takes : (ADDED in 1.10) $ request : "request" -> 32 hex characters, the request's own random id $ panel : {"state":1|0|null,"at":unix time|null} -> the panel's last APPLIED state (1 on / 0 off as last switched successfully and when; null = nothing recorded yet, e.g. right after a start of the Touch) - a record of what was done, not a live reading $ item : {"id":"id","name":"name"} -> a visualizer, meter, wave, fluid, saver or clock; the id or the name (any case) is what /visualizer, /meter, /wave, /fluid, /saver and /clock take $ wake : Every command that shows something switches the panel on; it then stays on while an activity of the Touch's rooms/zones runs (Screen setting While an activity runs = Stay on), else until the Screen settings' Off after (at night 60 s); end it with /saver or /panel {"state":0} $ accepted : A command answers that the request is ACCEPTED, not that the panel is on: the screen and the panel take it normally within 2 s; /running shows what was applied. The newest request wins: a later request may replace one not taken yet. Each request is taken once; one older than 30 s is not taken. A tap on the screen brings the screen back, as from any idle screen > /alive : No Data -> Returns Touch Name and Ident -> See Data Structure 'name' and 'ident' (no token) ----------------- | Login Command | ----------------- > /login : Data {"username":"username","password":"password"} -> Returns Token -> See Data Structure 'token' (no token) ----------------- | Info Commands | ----------------- > /version : Empty Data {} -> Returns Version -> See Data Structure 'version' > /places : Empty Data {} -> Returns the rooms / zones this Touch belongs to, per connected product, as picked on the Touch under Integrate (one or more): {"control":{"ident":"...","name":"...","places":[place, ...]} | null, "hub":{"ident":"...","name":"...","places":[place, ...]} | null} place = {"kind":"room"|"zone","id":N,"name":"..."}; null = not connected to that product. Read only; never the connection's token or address. Any data other than {} -> error 4 : (ADDED in 1.08) > /running : Empty Data {} -> Returns what the screen shows now: {"show":show,"which":"which","since":unix time,"age":seconds,"panel":panel} : show = a page as in Data Structure 'show', or "visualizer", "meters", "waves", "fluids", "saver", "clock", "dimmed" or another page of the screen; which = the visualizer, meter, wave, fluid, saver or clock id, "" when none : since = when the screen started showing it; age = seconds since the screen last reported: an unchanged report is renewed every 30 s, so a running screen's age goes up to ~30; more than 45 = the screen is not running : panel as in Data Structure 'panel' - separate from age (the page can run with the panel off) : The screen has not reported yet (its browser not running) but a panel state is recorded -> {"show":null,"which":"","since":null,"age":null,"panel":panel} : Neither reported nor a panel state recorded -> error 7 ------------------- | Screen Commands | ------------------- > /screen : Data {"show":show} -> Shows that page, the panel on -> Returns {"accepted":1,"request":request,"show":show} : See Data Structure 'show', 'request', 'wake' and 'accepted' : Unknown show, or any other field -> error 4, nothing is published, nothing wakes > /panel : Data {"state":1} -> The panel on, the page unchanged -> Returns {"accepted":1,"request":request,"state":1} : Data {"state":0} -> The panel off, the page unchanged (shown again on the next wake) : Empty Data {} -> Returns the panel -> See Data Structure 'panel' : state other than 0/1 -> error 4 > /brightness : Empty Data {} -> Returns {"brightness":10-100} : Data {"brightness":10-100} -> Sets it -> Returns {"brightness":10-100} : Other value or field -> error 4 : (ADDED in 1.07) > /inputs : Empty Data {} -> Returns {"inputs":[input, input]} -> the audio inputs that are there, [] when none, e.g. {"inputs":[{"id":1,"name":"Input 1","level":0,"sound":1},{"id":2,"name":"Input 2","level":-3,"sound":0}]} -> See Data Structure 'input'. Read only; any data other than {} -> error 4 : (ADDED in 1.10) > /input_level : Empty Data {} -> Returns {"level":-12..12|null,"inputs":[id, ...]} -> the inputs the Input level moves now and the level of the first of them: the input that has sound, else every input there (as the screen's Volume slider in Input level mode); null and [] = no input there : Data {"level":-12..12} -> Sets those inputs to it -> Returns {"level":N,"inputs":[id, ...]} (the inputs set). The customer's Input level: an adjustment on top of the installer's Input 1 / Input 2 level ($ input level), 0 = that level; the same setting as the app's and Now Playing's Input level : (CHANGED in 1.12: relative to the installer's level, was the installer's level itself) : and optional {"input":id or name} -> only that input (read or set) : level not a whole number -12..12, input not a number or text (also "input":null), or any other field -> error 4; an input that is not there, or none there to set -> error 5, nothing is written : (ADDED in 1.10) > /demo : Empty Data {} -> Returns {"kind":"visualizer|meters|waves|fluids","every":seconds,"repeat":0|1,"random":0|1, "running":0|1} : Data {"kind":"visualizer|meters|waves|fluids"} + optional "every" (5,10,15,20,30,60,120,300), "repeat" 0|1, "random" 0|1 -> Starts that Demo from the first -> Returns as {} : Data {"running":0} -> Stops it -> Returns as {} : Other value or field -> error 4 : (ADDED in 1.07) : A running Demo ends when anything else is chosen: every /screen, /panel off, /visualizer, /meter, /wave, /fluid, /saver and /clock command stops it (a /demo start only wakes the panel). When that stop cannot be saved, the command answers error 7 "Shown, but the running Demo could not be stopped" and its data still names the request it published : (ADDED in 1.09) ---------------------------------------------- | Visualizer, Meter, Saver and Clock Commands | ---------------------------------------------- > /visualizers : Empty Data {} -> Returns {"visualizers":[{item1, item2, ...}]} -> See Data Structure 'item' > /visualizer : Data {"visualizer":"id"} or {"visualizer":"name"} -> Shows that visualizer of the running activity, the panel on -> Returns {"accepted":1,"request":request,"visualizer":"id","running":1} : Empty Data {} -> the one set on the Touch, the same for every activity: the one shown last, else the one it opens with ("visualizer":"") : and optional {"running":0} -> Ends it: the screen back to its normal page, only when it shows it now (and, when named, that one) and the screen is running (/running age 45 or less); otherwise nothing is published and stopped = 0. Accepted is not applied: /running shows the result -> Returns {"accepted":1,"request":request|null,"visualizer":"id", "running":0,"stopped":1|0} > /meters : Empty Data {} -> Returns {"meters":[{item1, item2, ...}]} -> the VU and digital meters (ids vu_*) > /meter : Data {"meter":"id"} or {"meter":"name"} -> Shows the Meters with that meter for the running activity, the panel on, as /visualizer -> Returns {"accepted":1,"request":request,"meter":"id","running":1} : Empty Data {} -> the one set on the Touch, the same for every activity: the one shown last, else the one it opens with ("meter":"") : and optional {"running":0} -> Ends it, only when the screen shows the Meters now (and, when named, that meter), as /visualizer -> Returns {"accepted":1,"request":request|null,"meter":"id","running":0, "stopped":1|0} : While it shows, /running reports {"show":"meters","which":"vu_..."} > /waves : Empty Data {} -> Returns {"waves":[{item1, item2, ...}]} -> the waves (ids waveform, waveform_m) > /wave : Data {"wave":"id"} or {"wave":"name"} -> Shows the Waves with that wave for the running activity (its Visualizer sound; only a Local input carries a waveform), the panel on, as /visualizer -> Returns {"accepted":1,"request":request,"wave":"id","running":1} : Empty Data {} -> the one set on the Touch, the same for every activity: the one shown last, else the first ("wave":"") : and optional {"running":0} -> Ends it, only when the screen shows the Waves now (and, when named, that wave), as /visualizer -> Returns {"accepted":1,"request":request|null,"wave":"id", "running":0,"stopped":1|0} : While it shows, /running reports {"show":"waves","which":"waveform..."} > /fluids : Empty Data {} -> Returns {"fluids":[{item1, item2, ...}]} -> the fluids (ids fluid_water, fluid_fire) > /fluid : Data {"fluid":"id"} or {"fluid":"name"} -> Shows the Fluids with that fluid for the running activity (its Visualizer sound; only a Local input carries a waveform), the panel on, as /visualizer -> Returns {"accepted":1,"request":request,"fluid":"id","running":1} : Empty Data {} -> the one set on the Touch, the same for every activity: the one shown last, else the first ("fluid":"") : and optional {"running":0} -> Ends it, only when the screen shows the Fluids now (and, when named, that fluid), as /visualizer -> Returns {"accepted":1,"request":request|null,"fluid":"id", "running":0,"stopped":1|0} : While it shows, /running reports {"show":"fluids","which":"fluid_..."} > /savers : Empty Data {} -> Returns {"savers":[{item1, item2, ...}]} > /saver : Data {"saver":"id"} or {"saver":"name"} -> Shows that screen saver, as /visualizer (Empty Data {} = the one set on the Touch, {"running":0} ends it) : The saver "art" (name "Art") floats public-domain paintings (Rijksmuseum) as the cover savers float covers; it needs no Control (ADDED in 1.11) : The cover savers ("album_covers", "movie_posters", "albums_movies", "vinyl") show the Control's own covers; a kind the Touch has none of (no Control, or none from it) shows the Touch's built-in demo pictures of that kind - never own and demo pictures in one saver (CHANGED in 1.11) > /clocks : Empty Data {} -> Returns {"clocks":[{item1, item2, ...}]} > /clock : Data {"clock":"id"} or {"clock":"name"} -> Shows that clock face, as /visualizer (Empty Data {} = the one set on the Touch, {"running":0} ends it) : See Data Structure 'request', 'wake' and 'accepted' : An unknown id or name -> error 5, nothing is published, nothing wakes; a field of another type or running other than 0/1 -> error 4