|====================================| | ArtiControl API Version 1.22 | | Written by: Michel van Osenbruggen | | CopyRight 2026 ArtiLED B.V. | |====================================| Control version : 1.57 Latest change : 06-10-2026 ======================= | Protocol Definition | ======================= Protocol : HTTP(S) Port : 80/443 URL : http(s)://control_ip/api DATA : POST with variables |=============| | Definitions | |=============| Room : Physical room in the house Zone : Control zone within a room (e.g. Main, Secondary) Activity : Named AV preset for a zone (e.g. Watch TV, Listen to Music) Device : AV device controlled by the system (TV, receiver, player, etc.) Device Type : Template defining a device's capabilities and commands Flow : Multi-step automation sequence Integration : External system connection (Hub, Fibaro, HA, Somfy, Sonos, Hue, Homey) Tool : Deployed instance of an equipment template (matrix, screen, relay) Trigger : Event source that can start activities or flows |===================| | Command Structure | |===================| > Command : /command + Token + Data -> example http://control_ip/api/activities $ Data : Depends on Command -> Send Data as json variable -> example: {"zone":1} or {"room":2} $ Token : Send as variable with each request token="Token" (Token is case sensitive) |=============| | 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 : Backend service error (daemon communication failure) ====================== | Response Structure | ====================== $ response : Command Response => { "success":1/0, "error":error Number, "error_text":"Error Text", "execution_time":time, "data": { data } } $ success : 1 = Command Successful, 0 = Error (See Error) $ error : Error Number (See List) $ error_text : Error Text $ execution_time : Time in milliseconds it took to execute the Command $ data : See Data Structure ================== | Data Structure | ================== $ activity : {"id":id,"name":"name","icon":"icon","zone_id":zone_id,"sort":sort,"disabled":0/1,"roon_zone":"roon_zone_id"} : roon_zone is the Roon zone this Activity plays in, from the Roon integration's zone mapping (its own zone first, then its room), and "" when it has none. It says the Activity CAN play Roon, whether or not anything is playing now, so a caller can offer music before there is any $ device : {"id":id,"name":"name","type":type_id,"type_name":"name","brand":"brand","ip":"ip","mac":"mac","room":room_id,"zone":zone_id,"disabled":0/1,"down":0/1,"fail_count":fail_count,"sort":sort,"icon":"icon"} $ device_type : {"id":id,"name":"name","brand":"brand","brand_id":brand_id,"category":"category","category_id":category_id,"protocol":protocol,"port":port} $ device_state : {"device_id":id,"key":"key","value":"value","updated":"datetime"} $ zone : {"id":id,"name":"name","icon":"icon","room_id":room_id,"sort":sort,"disabled":0/1,"active_activity":activity_id,"now_playing":{unit_now_playing}} $ room : {"id":id,"name":"name","icon":"icon","sort":sort,"disabled":0/1,"now_playing":{unit_now_playing}} $ light : {"id":id,"name":"name","icon":"icon","places":[{"room":room_id,"zone":zone_id}, ...],"sort":sort,"pages":1/0,"source":{light_source},"state":"ok/missing/no_source","missing_since":"datetime" OR null,"checked_at":"datetime" OR null,"capabilities":{capabilities} OR null} (1.17; places 1.18) : One entry of Admin > Lights: a lighting target of ONE source, with Control's own name, icon, places, pages = shown on pages (1, the default) or not, sort = the order in Lights : places (1.18, replaces 1.17's room and zone): every Control room / zone the entry belongs to - a zone (zone > 0, room = that zone's room) or a room without zones (zone 0); [] = none (only a fixed Light card uses it). One entry may be in several zones (e.g. both zones of one room) : state "ok" = the item was present at the last ANSWERED list of its kind; it does NOT say the source answers now (see checked_at and source.status). "missing" = an answered list no longer has it (since missing_since). "no_source" = its integration was removed : checked_at = the time of the last answered list of its kind (null: never) : capabilities in the Hub's names (ArtiAPI 'capabilities': state, brightness, white, rgb, rgbw, hsv, palette, segments, channels, scenes, modes; a light also leds), as the source last reported them; a direct Hue entry in the same names (state, brightness, white, rgb; channels 0; scenes = the Hue scenes of that room or zone; modes 0); a Fibaro light (1.19) exactly as ArtiAPI 'capabilities' of the Hub's light types 31 (switch: state), 32 (dimmer: state, brightness) and 33 (RGBW: state, brightness, rgb, rgbw), its class decided from the Home Center's device at every answered check; a Homey light (1.20) as the Hub's types 41 (switch), 42 (dimmer), 43 (white: state, brightness, white), 44 (colour: state, brightness, rgb, hsv) and 45 (colour and white), from the device's capabilities; a Home Assistant light (1.21) as the Hub's types 51 (switch.* or [onoff]), 52 (dimmer), 53 (white), 54 (colour), 55 (colour and white), 56 (RGBW), 57 (RGBW and white), from its supported_color_modes; palette, segments, channels, scenes and modes 0, leds []. null = not known yet or not reported. scenes may be null on its own (not known) $ light_source : {"integration":integration_id,"kind":"hub_room/hub_zone/hub_light/hue_room/hue_zone/hue_light/fibaro_light/homey_light/ha_light","id":"source_id","name":"name","status":"ok/unreachable/disabled/removed"} (1.17; fibaro_light 1.19; homey_light 1.20; ha_light 1.21) : id = the source's own id (a Hub id as text, a Hue UUID, a Fibaro Home Center device id as text, a Homey device id, a Home Assistant entity id: light.* or switch.*); name = the source's current name. status = the source now: ok (its list of this kind answered at the last check), unreachable (it did not), disabled (disabled under Integrate: not asked), removed (no such integration) $ flow : {"id":id,"name":"name","icon":"icon","disabled":0/1,"running":true/false,"last_run":"datetime"} $ profile : {"id":id,"name":"name","icon":"icon","active":0/1,"protected":0/1,"role_id":role_id} $ integration : {"id":id,"name":"name","type":type_id,"icon":"icon","disabled":0/1,"ip":"ip","port":port,"protocol":protocol,"status":status} $ trigger : {"id":id,"name":"name","icon":"icon","disabled":0/1,"direction":1/2/3,"url":"url","interval":seconds,"last_polled":"datetime","last_error":"text","fail_count":count} (direction: 1=Inbound, 2=Outbound, 3=Both) $ mode : {"id":id,"name":"name","image_url":"image_url"} $ now_playing : {"zone":"zone","title":"title","artist":"artist","album":"album","image":"image_url","source":"source","genre":"genre","year":"year","state":"state"} $ unit_now_playing : {"state":"state","title":"title","artist":"artist","album":"album","image_url":"image_url","duration":seconds,"position":seconds,"source":"source","year":"year","genre":"genre","rated":"rated","rating":"rating","directors":"directors","actors":"actors","synopsis":"synopsis"} (null = nothing playing) : source of unit_now_playing: "roon", "sonos", "spotify", "appletv", "androidtv", "hifirose", "rvolution", "kaleidescape" or "zidoo" ("" when nothing plays). Spotify since Control 1.54: an Activity with a Spotify step. /dashboard shows the same source as a name: Roon, Sonos, Spotify, Apple TV, Android TV, HiFi Rose, R_Volution, Kaleidescape, Zidoo $ screen : {"name":"name","items":[{screen_item, ...}]} $ screen_item : {"type":"icon/text/media_player","icon":"icon","text":"text","x":x,"y":y,"width":width,"height":height,"button":"button_name","page":page_id,"item":item} (page: 0 = Activity) ============ | Commands | ============ ----------------- | Alive Command | ----------------- > /alive : No Data, No Token -> Returns Control Name and Ident -> {"name":"name","ident":"ident"} ----------------- | Login Command | ----------------- > /login : Data -> {"username":"username","password":"password"} -> Returns Token -> {"token":"token"} ----------------- | Info Commands | ----------------- > /version : Data -> {} -> Returns Control Version -> {"version":"version"} -------------------- | Dashboard Command | -------------------- > /dashboard : No Data -> Returns Active Activities and Now Playing -> {"activities":[{activity, ...}],"now_playing":[{now_playing, ...}]} : Note: Activities with active=1 are currently running. Now Playing shows media info from all active sources. ------------------- | Zone Commands | ------------------- > /zones : Data -> {} or {"room":room_id} -> Returns Zones -> {"zones":[{zone, ...}]} See Data Structure 'zone' > /rooms : Data -> {} -> Returns Rooms -> {"rooms":[{room, ...}]} See Data Structure 'room' : Note: zone.active_activity shows which activity is running (0 = none) ----------------------- | Activity Commands | ----------------------- > /activities : Data -> {"zone":zone_id} or {"room":room_id} -> Returns Activities -> {"activities":[{activity, ...}]} See Data Structure 'activity' > /activity : Data -> {"action":"start","activity":activity_id} -> Starts Activity -> Returns Response : Optional {"origin":"hub"} marks the run as hub-originated: Hub steps inside the activity are then skipped (loop guard, one hop allowed) > /activity : Data -> {"action":"start","name":"name"} -> Starts every enabled Activity of that name, one per zone -> Returns {"name":"name","activities":[activity_id, ...],"started":count} > /activity : Data -> {"action":"stop","activity":activity_id} or {"action":"stop","zone":zone_id} -> Stops Activity -> Returns Response > /intermission : Data -> {"action":"start","zone":zone_id} or {"action":"start","room":room_id} -> Starts the Intermission of a zone, or of a room without zones (runs its activity's On Intermission) -> Returns Response > /intermission : Data -> {"action":"end","zone":zone_id} or {"action":"end","room":room_id} -> Ends the Intermission, Continue (runs On Intermission End) -> Returns Response : zone or room: exactly one of them, a JSON integer above 0 (not a string, float, 0 or negative). A room that has zones is refused: give the zone : Optional {"origin":"hub"} marks the transition as hub-requested: Hub flow steps inside On Intermission / On Intermission End are then skipped (loop guard, one hop allowed) : Success means the Activity Service took the step. Nothing happens, and the service logs why, when Intermission is switched off in Node -> Settings, nothing runs in that zone or room, or it is already in (or not in) an intermission : Error 3: no action, or no zone or room. Error 4: any other invalid data (both a zone and a room, a wrong id type or value, an unknown unit, a room with zones, an unknown action or origin). Error 5: the Activity Service did not take the step ----------------------- | Music Commands | ----------------------- > /music : Data -> {} -> Returns what this Control can play -> {"rooms":[{"name":"name"}, ...],"devices":[{"name":"name"}, ...],"playlists":[{"source":"source","key":"key","title":"title","name":"name","type":"type"}, ...],"categories":[{"id":id,"name":"name"}, ...],"clips":["file", ...],"genres":{"yours":["genre", ...],"tree":{"main":["sub", ...]}},"tidal":[{"kind":"playlist|album","uri":"tidal:kind:id","title":"title","subtitle":"artist"}, ...],"roon":{"zones":[{"id":"zone_id","name":"name"}, ...]},"heos":{"players":[{"device_id":id,"name":"name"}, ...],"sources":[{"sid":sid,"name":"name","type":"type"}, ...],"error":"","picks":[{"kind":"album|playlist","title":"title","subtitle":"artist","sid":5,"cid":"Albums-14713344","from":"My Sonos|Hearted"}, ...],"picks_error":""},"stations":[{"uri":"tunein:s9483","name":"name"}, ...],"deezer":[{"kind":"album|playlist|artist|track","title":"title","subtitle":"artist","uri":"deezer:album:14713344","from":"My Sonos|Hearted"}, ...],"deezer_error":"","zidoo":{"players":[{"device_id":id,"name":"name"}, ...],"error":""}} : rooms are the Sonos rooms this Control sees, devices the Spotify Connect devices; playlists carries every music source's playlists and favorites, source being roon, sonos_favorite, spotify or spotify_album : type is a Sonos favorite's kind as Sonos files it: Album, Artist, Playlist, Track or Radio (the Sonos actions album, artist, playlist, song and station each play one of that kind). Sonos's old saved queues (sonos_playlist) are no longer listed: the Sonos app dropped them in 2024 : tidal is the customer's own TIDAL, for a Sonos Play TIDAL: their playlists and saved albums, empty when nobody is signed in to TIDAL on this Control : genres are for a Spotify Play Genre: "yours" the genres of this account's own albums, most common first, and "tree" the main genres with their subgenres : heos is for a HEOS step: players are the Control devices linked to a HEOS player (only those can be a HEOS step's player; a device links itself when HEOS reports a player at its own address), sources the music sources the HEOS system offers (Deezer 5, TIDAL 10, Qobuz 30, ... as HEOS lists them). error says why the sources are empty when the Control's HEOS service did not answer : heos.picks (1.11) are the Deezer and TIDAL albums and playlists the customer has - My Sonos's, then the albums hearted on Control - in the forms HEOS plays them: Deezer sid 5 Albums- / Playlist-, TIDAL sid 10 LIBALBUM- / LIBPLAYLIST-. A HEOS step plays one as play_source with that sid and cid (and its title as label), exactly as a browsed one. picks_error (1.13) names a part that could not be read (My Sonos, or the hearted albums); what could be read is still in picks : deezer (1.14) is the customer's Deezer for a Sonos Play Deezer (action deezer): My Sonos's Deezer albums, playlists, artists and songs, then the Deezer albums hearted on Control, each by its address. deezer_error names a part that could not be read; what could be read is still listed : stations (1.10) are the favourite TuneIn stations, Control's own record as the Music Player's TuneIn tab shows them, for a Sonos Play TuneIn (action tunein) and a HEOS Play a station (sid 3). uri is the station's address: tunein: and its TuneIn id, the same id on Sonos, HEOS and TuneIn. A station taken off is not listed : roon is for a Roon step: the Roon zones this Control sees. Roon's artists, genres, composers and albums are NOT here — there are thousands of each, and they would be nine tenths of this answer; ask for them a page at a time with "names" and "albums" below > /music : Data -> {"names":{"service":"roon","of":"artists","offset":0}} -> Returns one page of those names -> {"names":["name", ...],"count":total,"offset":next_offset,"done":true|false} : of is artists, genres or composers. For the subgenres of one main genre, add "under":"Electronic" to of:"genres" : offset is where to start, 0 first; the answer's offset is where the next page starts and done says there is no next page. A page is at most 100 names : Error 4: an unknown service, an unknown "of", or an offset that is not a number. Error 7: the service could not be asked (the reason is in error_text) > /music : Data -> {"albums":{"service":"roon","offset":0}} -> Returns one page of Control's album list for that service -> {"albums":[{"id":id,"title":"title","subtitle":"artist"}, ...],"count":total,"offset":next_offset,"done":true|false} : id is the album's id in Control's own list, which is what a Roon step saves under album_id. Paged like names, at most 100 an answer > /music : Data -> {"albums":{"service":"zidoo","device":device_id,"offset":0}} -> Returns one page of that Zidoo's albums (1.16) -> {"albums":[{"key":"zidoo:device_id:sha1","title":"title","subtitle":"artist","playable":true|false}, ...],"count":total,"offset":next_offset,"done":true|false,"device":device_id} : Error 4: an unknown service or an offset that is not a number. Error 7: the album list could not be read > /music : Data -> {"search":{"service":"spotify","kind":"album","q":"violator"}} -> Returns what Spotify finds, for a step's picker -> {"results":[{"uri":"spotify:album:id","title":"title","subtitle":"artist"}, ...]} : kind is album, track or artist; at most 10 results; searched with this Control's own Spotify connection. The uri is what a Spotify step saves under value (Play Album / Play Track / Play Artist (search), Add to Queue) : Error 4: a service other than spotify or tunein, another kind, no words, or a search sent together with a play. Error 7: Spotify is not connected or did not answer (the reason is in error_text) > /music : Data -> {"search":{"service":"tunein","kind":"station","q":"npo"}} -> Returns the TuneIn stations TuneIn finds, for a step's picker (1.10) -> {"results":[{"uri":"tunein:s9483","title":"title","subtitle":"subtitle"}, ...]} : kind is station; at most 10 results; TuneIn's public search, as the Music Player's TuneIn tab searches. The uri is what a Sonos tunein step saves under value; a HEOS step plays it as sid 3 with the id after tunein: as mid : Error 4: another kind, no words, or a search sent together with a play. Error 7: TuneIn did not answer > /music : Data -> {"search":{"service":"apple","kind":"item","q":"violator"}} -> Returns what Apple Music finds, for a Sonos Play Apple Music (1.14) -> {"results":[{"uri":"apple:album:1440857781","title":"title","subtitle":"artist · album"}, ...]} : kind is item: albums (apple:album:), songs (apple:song:) and artists, played as their radio (apple:station:); Apple's public catalogue. Only when Apple Music is linked in Sonos. One part failing while another found things: the results and a "warning"; everything failing: Error 7 : Error 4: another kind, no words, or a search sent together with a play. Error 7: Apple Music is not linked in Sonos, or Apple did not answer > /music : Data -> {"search":{"service":"heos","kind":"container","q":"violator"}} -> Returns the Deezer and TIDAL albums and playlists HEOS finds, for a HEOS step's picker (1.11) -> {"results":[{"uri":"heos:5:Albums-6709168","title":"title","subtitle":"artist · service"}, ...]} : kind is container; at most 10 results; searched through this Control's HEOS player, in the Deezer and TIDAL it is signed in to, albums and playlists that play whole. uri is heos:: - a HEOS step saves them as sid and cid : when one service could not be searched while another found things, the answer also carries "warning":": " (1.13); a search with no results in which a service failed is Error 7, never an empty success : Error 4: another kind, no words, or a search sent together with a play. Error 7: HEOS did not answer, or the search failed (error_text says why) > /music : Data -> {"browse":{"service":"heos","sid":5,"cid":"container","offset":0}} -> Returns one page of that HEOS source, for a step's picker -> {"items":[{"name":"name","cid":"cid","mid":"mid","container":"yes|no","playable":"yes|no","type":"type","image_url":"url"}, ...],"count":total,"offset":next_offset,"done":true|false} : sid is a source from the lists (a JSON integer above 0); cid the container to open, left out for the source's top level. A page is at most 50; offset as for names. A playable container (a playlist, an album) is saved as sid + cid, a track or station as sid + mid (with the cid it came from) : read-only; it never travels with a play or another page kind : Error 4: another service, a sid that is not a number above 0, an offset that is not a number, or a browse sent together with anything else. Error 7: the page could not be read - error_text begins with the class (offline, refused, failed-before-send, ...) > /music : Data -> {"sonos":{"action":"album","value":"Violator","room":"Keuken"}} -> Plays that Sonos favorite in that Sonos room -> Returns Response : a favorite is played by its title with the action of its kind: album, artist, playlist, song or station ("favorite", any kind, is kept for steps saved before the kinds existed). The editors call station "Play TuneIn"; its saved value stays station > /music : Data -> {"sonos":{"action":"tunein","value":"tunein:s9483","label":"NPO Radio 2","room":"Keuken"}} -> Plays that TuneIn station in that Sonos room (1.10) -> Returns Response : value is the station's address from stations or a TuneIn search (tunein:s9483), or its bare id (s9483); label is its name, what the room shows (the id when left out). Played as the Music Player plays a TuneIn station. "sonos:all": the first room plays it and every other room joins : Error 7: a value that is not a TuneIn station id, or the Sonos service refused it > /music : Data -> {"sonos":{"action":"deezer","value":"deezer:album:14713344","label":"Oxygene 3","mode":"now","room":"Keuken"}} -> Plays that Deezer item in that Sonos room (1.14) -> Returns Response : value is deezer:album|playlist|track|artist: (from deezer); an artist plays as their top tracks; a track plays now. Played through the Deezer account linked in Sonos, as the Music Player plays it > /music : Data -> {"sonos":{"action":"apple","value":"apple:album:1440857781","label":"Violator","mode":"shuffle","room":"Keuken"}} -> Plays that Apple Music item in that Sonos room (1.14) -> Returns Response : value is apple:album|song|station: (from an Apple Music search); a station is the artist's radio, named by label; a song plays now. Error 7 for another value, as for deezer > /music : Data -> {"spotify":{"action":"play_playlist","value":"spotify:playlist:id","device":"Keuken"}} -> Plays it on that Spotify device or Sonos room -> Returns Response > /music : Data -> {"roon":{"action":"playlist","playlist":"Relax","zone_id":"1601..."}} -> Plays it in that Roon zone -> Returns Response > /music : Data -> {"zidoo":{"action":"album","device_id":8,"album_key":"zidoo:8:sha1"}} -> Plays it on that Zidoo (1.16) -> Returns Response : device_id (a JSON integer or digits, above 0), action (album, category, favorites, anything, pause, stop), album_key (album), category_id (category); data.result: observed | unconfirmed | refused | busy | sent | already_stopped, also with Error 7 > /music : Data -> {"heos":{"action":"play_source","device_id":12,"sid":5,"cid":"container","mode":"now"}} -> Plays it on that HEOS player -> Returns Response : device_id is the Control device of the HEOS player (a JSON integer or digits, above 0; never a name). The HEOS step's own keys: action (play_source, play_station, play_favorite, play_input, play, pause, stop, next, previous, volume, volume_up, volume_down, mute, unmute, shuffle_on, shuffle_off, repeat_off, repeat_one, repeat_all, join, leave), sid, cid, mid, mode (now | shuffle), preset (a HEOS favourite's number), input, value (a volume 0-100), leader_device_id (join: the player to join), label, name (1.10: play_favorite, the favourite's name at that number), tracks (1.12: play_source) : tracks "1" (1.12): an album or playlist HEOS will not queue whole (Qobuz's: playable no in a browse) is played as its tracks. The step names the container (sid + cid); the Control's HEOS service reads its playable tracks when it plays - so an edited playlist plays what it holds then - and plays the first, the rest after it. More than 100 tracks: refused ("This list has N tracks; HEOS plays at most 100 at a time from here. Open it and play from a track."), nothing sent. None playable: refused, nothing sent. tracks with a mid, or without a cid: refused : a HEOS favourite is played by its number; with a name (or, when there is none, the label) the HEOS service first checks that the favourite at that number still has that name, and refuses a list that changed ("the HEOS favourites changed - reload them") instead of playing another favourite. Without either, the number alone is played : a TuneIn station is play_station with sid 3 and the station's TuneIn id as mid (the id after tunein: in stations) : ⛔ success 1 ONLY when the Control's HEOS service confirmed it: a play seen playing what was asked, a setting read back, a group read back. Otherwise error 7 and error_text BEGINS with the class: refused (the player's own refusal) | failed-before-send (nothing reached the player) | sent-unconfirmed (sent; whether it happened is not known - it is never sent again) | offline | disabled | signed_out (Deezer, TIDAL or Qobuz without the player's account) : The music step of an Activity, run on its own: the keys are the step's, as Control saves them (Sonos: action, value, mode, room, volume, category_id, spotify_action, tidal_action, label; Spotify: action, value, mode, device, category_id, genre_main, label; Roon: action, zone_id, mode, playlist, album_id, artist, genre, genre_main, composer, category_id, value, label) : room (Sonos), device (Spotify) or zone_id (Roon) is REQUIRED here and may not be empty: a caller outside an Activity has no room or zone mapping to fall back on. "sonos:all" is every Sonos room : Roon names what it plays under its own key, as Control's own step saves it: playlist and composer by name, artist by name, genre by name with genre_main for a subgenre, album_id for an album of Control's list, category_id for a category; value carries the volume, the zone to transfer to, or the sleep minutes : Optional {"origin":"hub"}: the only origin accepted; it does not change what plays : Error 3: no data. Error 4: not exactly one of sonos, spotify, roon, heos or zidoo, a config that is not an object, no action, no room, device, zone or device_id (heos, zidoo: a device_id that is not a number above 0), or an unknown origin. Error 7: the step did not play (the reason is in error_text) ----------------------- | Movie Commands | ----------------------- > /movies : Data -> {} -> Returns what a Play Movie step can name on this Control -> {"players":[{"id":device_id,"name":"name","adapter":"kaleidescape|zidoo"}, ...],"categories":[{"id":id,"name":"name"}, ...]} : players are only the movie players that play from the library (a Kaleidescape player of a system under Integrations); categories are the movie categories, in their order > /movies : Data -> {"titles":{"offset":0,"player":device_id}} -> Returns one page of the library's titles -> {"titles":[{"id":title_id,"name":"name","year":year}, ...],"count":total,"offset":next_offset,"done":true|false,"player":device_id} : only titles still in the library, by name then year. A page is at most 100; offset is where to start, 0 first, a JSON integer (not a string); the answer's offset is where the next page starts and done says there is no next page > /movies : Data -> {"people":{"kind":"director","offset":0,"player":device_id}} -> Returns one page of the library's directors or actors -> {"names":["name", ...],"count":total,"offset":next_offset,"done":true|false,"player":device_id} : kind is director or actor; paged like titles. The names are the ones a Play Movie step for a person finds (actors: Kaleidescape's cast, then the online billed cast) > /movies : Data -> {"browse":{"activity":activity_id,"folder":"","offset":0,"limit":20}} -> Returns one folder of the films of an Activity's movie player (1.22) -> {"id":"folder","title":"title","items":[item, ...],"count":total,"offset":offset,"player":{"id":device_id,"name":"name","adapter":"kaleidescape|zidoo"}} : the movie player is the Activity's own (its first step that plays films, else its media player with the Playback role); only that player's films, never the whole library; hidden films and people are left out : folder "" is the menu: recent, fav, cats and az, only those with films. recent = recently played, then recently added (18 each), newest first. fav = fav:movie (hearted films), fav:director and fav:actor (hearted people) -> director:name or actor:name (that person's films). cats -> cat:category_id. az -> az:A ... az:Z and az:# (only letters with films) : item = {"id":"movie:title_id","kind":"movie","title":"name","subtitle":"year","cover":"file of its small cover, empty for none"} or {"id":"folder","kind":"folder","title":"name","icon":"uc:icon"} : films are by name, a leading The, A, An, De, Het or Een not counted (The Thing is under T); case and accents are folded : limit 1-50 (default 20), offset from 0 (default 0), both JSON integers; count is the folder's total > /movies : Data -> {"search":{"activity":activity_id,"query":"words","offset":0,"limit":20}} -> Returns the films of an Activity's movie player that match (1.22) -> {"id":"search","title":"Search","items":[item, ...],"count":total,"offset":offset,"player":{...}} : matches the title, director, cast and genres, case and accents folded, as Movies > Player's search; query at most 80 characters; nothing typed finds nothing : Read only: nothing here reaches a player; a film is played with /movie_step. One list per call : Error 4: more than one list, an unknown list or kind, an offset or limit out of range or not an integer, or no activity_id. Error 5: the Activity plays films on no player whose library Control shows. Error 6: not a folder. Error 7: the movie library could not be read > /movie_step : Data -> {"config":{"what":"movie","title_id":title_id,"device_id":device_id,"option":"resume"},"activity_id":0,"service":"kaleidescape|zidoo"} -> Plays it: the Play Movie step (45) of an Activity or Flow, run on its own -> Returns Response : config is the step's own, as Control saves it: what (movie, category, favorites, person or anything), title_id and title_name (movie), category_id (category), person_kind (director or actor) and person_name (person), device_id, option : option is resume (default), beginning, chapter:N or only:N (N 1-999), play (zidoo); chapter and only are for a named movie only, a film from a category, favorites, a person or anything plays resume or beginning : device_id is the movie player. With none (0), the movie player of activity_id's Activity is used; a caller outside an Activity (a Hub) MUST name one. A named player that is gone, disabled or cannot play from the library is refused, never replaced by another : A pool (category, favorites, person, anything) is narrowed to the films that play on that player BEFORE anything is sent; one of them is chosen at random and played once - never a second film after a first that failed : Success only when the player was seen playing the film (and a chapter option was done). The call can take up to about 70 seconds (waking the player, the option); give it a read timeout above that : Optional {"origin":"hub"} is accepted and does not change what plays : Error 3: no data (it must be a POST). Error 4: the step's config is invalid or nothing played (the reason is in error_text). Error 1: the movie library could not be read ----------------------- | Screen Commands | ----------------------- > /screens : Data -> {"activity":activity_id} -> Returns the Activity's Screens -> {"screens":[{screen, ...}]} See Data Structure 'screen' > /screen_button : Data -> {"activity":activity_id,"page":page_id,"item":item} -> Presses a Screen Button -> Returns Response ----------------------- | Profile Commands | ----------------------- > /profiles : No Data -> Returns Profiles -> {"profiles":[{profile, ...}]} See Data Structure 'profile' > /profile : Data -> {"profile":profile_id} or {"profile":"profile_name"} and {"action":"activate"} or {"action":"deactivate"} and optional {"source":"trigger"} -> Activates or deactivates the profile -> Returns Response : Note: Profiles are mutually exclusive within a Role (activating one deactivates the others in that role). A Protected profile only changes when source is "trigger". Each profile may run a configured Control action (Activity, Flow, device command, or HTTP) on activate and on deactivate. --------------------- | Device Commands | --------------------- > /devices : Data -> {} or {"zone":zone_id} or {"room":room_id} -> Returns Devices -> {"devices":[{device, ...}]} See Data Structure 'device' > /device_types : Data -> {} or {"brand":brand_id} or {"category":category_id} -> Returns Device Types -> {"device_types":[{device_type, ...}]} See Data Structure 'device_type' > /states : Data -> {} or {"device":device_id} -> Returns Device States -> {"states":[{device_state, ...}]} See Data Structure 'device_state' > /command : Data -> {"device":device_id,"command":"command_name"} -> Sends Command to Device -> Returns Response > /button : Data -> {"zone":zone_id,"button":"button_name"} -> Sends Button Press via Activity Context -> Returns Response -------------------- | Light Commands | -------------------- > /lights : Data -> {} or {"zone":zone_id} or {"room":room_id} -> Returns the Lights entries -> {"lights":[{light, ...}]} See Data Structure 'light' (1.17) : zone: the entries with a place in that zone; room: the entries with a room-level place in that room (zone 0), as /devices (1.18: on places). In Lights order (sort, then id). Read-only: nothing is asked of a source and nothing is switched. No Lights entries (or a Control before them): an empty list ------------------- | Flow Commands | ------------------- > /flows : Data -> {} -> Returns Flows with running state -> {"flows":[{flow, ...}]} See Data Structure 'flow' > /flow : Data -> {"flow":flow_id,"action":"start"} or {"flow":"flow_name","action":"start"} -> Starts or Stops Flow -> Returns Response : Optional {"origin":"hub"} marks the run as hub-originated: Hub flow steps inside the flow are then skipped (loop guard, one hop allowed) ------------------- | Mode Commands | ------------------- > /modes : Data -> {"zone":zone_id} or {"room":room_id} -> Returns Modes -> {"modes":[{mode, ...}]} See Data Structure 'mode' > /mode : Data -> {"mode":"mode_name"} -> Starts Mode -> Returns Response -------------------------- | Integration Commands | -------------------------- > /integrations : Data -> {} -> Returns Integrations -> {"integrations":[{integration, ...}]} See Data Structure 'integration' ------------------------ | Trigger Commands | ------------------------ > /triggers : Data -> {} -> Returns Triggers -> {"triggers":[{trigger, ...}]} See Data Structure 'trigger' Inbound Webhook (reactive — an external system pushes an event to Control): > /trigger/{name} : Key -> ?key={trigger_key} (or POST field 'key', or 'X-Trigger-Key' header) : Data -> ?data={json} (or POST field 'data', or raw JSON request body) e.g. {"status":"armed"} : Matches the payload against the trigger's States and fires the matching action (else the Default Action) -> {"triggered":true/false,"state":"state_name"} : The {name} and key are shown on the trigger's Connection tab. Allowed when Direction is Inbound or Both, the trigger is enabled, and the key matches. > /trigger_test : Data -> {"trigger":trigger_id} (optionally url/method/auth/headers to test edits before saving) -> Polls the outbound URL once and returns the response plus which States match -> {"response":...,"states":[{"name","field","current_value","expected_value","matches"}]} : Data -> {"trigger":trigger_id,"regenerate_key":true} -> Generates a new inbound key -> {"new_key":"key"} : Note: Trigger actions are Control-native — Activity, Flow, Device command (+ optional parameter, e.g. volume), HTTP, Activate Profile, or Deactivate Profile. A trigger has a Default Action plus any number of States (field/operator/value -> one action); the first matching State fires. Chain multiple actions by pointing the action at a Flow. ----------------------- | Variable Commands | ----------------------- > /variable_get : POST -> name=variable_name -> Returns Variable Value from Redis -------------------------- | HTTP Request Command | -------------------------- > /http_request : POST -> url, method (GET/POST/PUT/DELETE), headers, body -> Proxies HTTP Request -> Returns Response : Note: Used by flows for HTTP REQUEST step type