|====================================| | ArtiAPI Protocol Version 1.64 | | Written by: Michel van Osenbruggen | | CopyRight 2026 ArtiLED B.V. | |====================================| Hub version : 1.79 Latest change : 11-09-2026 ======================= | Protocol Definition | ======================= Protocol : HTTP(S) Port : 80/443 URL : http(s)://hub_ip/api DATA : POST with variables |=============| | Definitions | |=============| House : All lights are located in a House Room : Every light can be located in only one Room Zone : Every light can be located in multiple Zones Strip : Whole led strip -> All leds (Number of leds) Multi Strip : Multiple led strips with channel -> All leds Segment : Part of a led strip -> Start, End -> Start and End (0-1000) Part : Part of a led strip -> Start, End -> Start and End (0-1000) (Used for different scaling with Palette Part) Multi Segment : Multiple Parts of a led strip -> Array of n x (Start, End) -> Start and End (0-1000) Pixel : Single pixel of a led strip -> Pixel number -> Pixel numbers (0-1000) Multi Pixel : Multiple pixels of a led strip -> Array of Pixel Numbers -> Pixel numbers (0-1000) Pixel String : Multiple pixels of a led strip with a start (msb, lsb) then followed by an Array of Pixel Numbers (0-1000) Palette : Collection of Colors -> Max 16 colors |===================| | Channel Structure | |===================| 0 : 0 = All Channels 1 : 1 = Channel 1 2 : 2 = Channel 2 3 : 3 = Channel 3 4 : 4 = Channel 4 12 : 12 = Channel 1 + 2 13 : 13 = Channel 1 + 3 14 : 14 = Channel 1 + 4 23 : 23 = Channel 2 + 3 24 : 24 = Channel 2 + 4 34 : 34 = Channel 3 + 4 123 : 123 = Channel 1 + 2 + 3 124 : 124 = Channel 1 + 2 + 4 134 : 134 = Channel 1 + 3 + 4 234 : 234 = Channel 2 + 3 + 4 |===================| | Command Structure | |===================| > Command : /command + Token + Data -> example http://hub_ip/api/rooms # Privileged Command : /command + Token + Data (requires admin rights) $ 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) ! Future Command : Command not done yet, coming soon |=============| | 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 (id does not exist) 6 : Command Invalid 7 : Command Execution Failed ====================== | Response Structure | ====================== $ response : Command Response => { "success":1/0, "error":error Number, "error_text":"Error Text", "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 | ================== $ name : {"name":"name"} -> Node Name $ ident : {"ident":"ident"} -> Node Ident $ version : {"version":"version"} -> Node Software Version $ state : {"state":state} -> State 0=Off, 1=On $ online : {"online":online} -> Node/Light Status 0=Offline, 1=Online $ active : {"active":active} -> Active 0=Not Active, 1=Active $ channel : {"channel":channel} -> See Channel Structure $ brightness : {"brightness":brightness} -> Brightness Value (0-255) $ hue : {"hue":hue} -> Hue Value (0-255) $ saturation : {"saturation":saturation} -> Saturation Value (0-255) $ color : {"color":"rgb(red,green,blue)"} $ colors : {"colors":["rgb(red,green,blue)","rgb(red,green,blue)", ....]} $ color_rgbw : {"red":red,"green":green,"blue":blue,"white":white} -> Values (0-255) $ color_rgb : {"red":red,"green":green,"blue":blue} -> Values (0-255) $ color_hsv : {"hue":hue,"saturation":saturation,"value":value} -> Values (0-255) $ color_w : {"temperature":temperature,"intensity":intensity} -> Values (0-255) $ segment : {"start":start,"end":end} -> Values (0-1000) $ part : {"start":start,"end":end} -> Values (0-1000) $ clear : {"clear":clear,"commit":commit} -> Values (0-1) $ house : {"power":power,"state":state} -> Aggregated across all rooms+zones (OR semantics) $ room : {"id":id,"name":"name","icon":"icon","status":"status","state":state,"power":power,"brightness":brightness,"colors":["rgb(red,green,blue)", "rgb(red,green,blue)", ...],"running":{"type":"scene/mode","name":"name"} OR null} $ zone : {"id":id,"name":"name","icon":"icon","channel":channel,"status":"status","state":state,"power":power,"brightness":brightness,"colors":["rgb(red,green,blue)", "rgb(red,green,blue)", ...],"running":{"type":"scene/mode","name":"name"} OR null} $ scene : {"id":id,"name":"name","image":"image","running":0/1,"type":type} -> Running 0=Done, 1=Running : type 1=White 2=RGB 3=Multi 4=Gradient 5=Advanced 6=RGBW 7=Code 8=HTTP 9=Hue 10=Flow 11=HSV 12=Custom $ custom_scene : {"scene_id":id,"scene_name":"name","scene_image":"image","scene_rooms":"csv","scene_zones":"csv","scene_flow_id":id,"members":[{member1, member2, ....}]} $ brightness_fade : {"mode":mode,"curve":curve,"start":start,"end":end,"time":ms,"step":step} -> Controller-run brightness ramp (0xa5) : mode 0=From current brightness, 1=From start. curve See Curves List (0-10). start/end 0-255 : time 1-65535 MILLISECONDS (two bytes). step 1-255. Brightness only; colour is not changed $ member : {"selection":"room/zone/light","target":id,"action":"action"} and the fields that action uses : action : on,off,white,warm_white,normal_white,cool_white,rgb,rgbw,hsv,brightness,palette,effect_rgb,effect_rgbw,software_fade,software_transition,software_fade_transition,hardware_fade,clear : optional per action : "channel","value","brightness","red","green","blue","hue","saturation","hsv_value","white","temperature","intensity","color_array","palette_scale","palette_alternate","palette_blend","palette_start_index","palette_end_index","effect_id","effect_clear","effect_step","effect_start_delay","effect_end_delay","fade_type","start","end","duration","curve","step" : software fade axes : "red_start","red_end","green_start","green_end","blue_start","blue_end","white_start","white_end","hue_start","hue_end","saturation_start","saturation_end","value_start","value_end","temperature_start","temperature_end","intensity_start","intensity_end","palette_start","palette_end","brightness_start","brightness_end","fade_brightness" : hardware_fade adds "fade_mode" 0/1 and reuses "curve","start","end","step" and "duration" : hardware_fade "duration" is SECONDS but caps at 65 -> the controller time field is two bytes of ms : "delay" seconds, float, 0-3600 -> runs this member that long after the scene starts : "selection" and "target" are required; an absent "action" is stored as "rgb" $ mode : {"id":id,"name":"name","image":"image","running":0/1} -> Running 0=Done, 1=Running $ light : {"id":id,"name":"name","icon":"icon","disabled":disabled,"down":down,"status":"status","power":power,"state":state,"brightness":brightness,"colors":["rgb(red,green,blue)", ...],"channels":{"1":["rgb(...)"],"2":["rgb(...)"],"3":["rgb(...)"],"4":["rgb(...)"]},"running":{"type":"scene/mode","name":"name"} OR null} : room/zone/light "status" reflects what was last set: - "Off" (state=0) - "Red"/"Green"/"Blue"/"Yellow"/"Orange"/"Cyan"/"Purple"/"Pink"/"White"/"Black" (color set) - "Cold White"/"Normal White"/"Warm White" (color_w set) - "Palette" (palette set) - "" (scene running) - "" (mode running) : room/zone "power" is OR of member lights' power (PSU on for nodes; always 0 for Hue/slave). : light "channels" is always present with keys "1","2","3","4". Each channel either has its own per-channel colors (set via zone_channel-scoped zones or /light SET with channel parameter), or falls back to the whole-light colors. : light "colors" is the whole-light view: bare :colors if no per-channel SETs have happened, else the bare :colors concatenated with each diverged channel (channel 1 red + channel 2 blue -> ["rgb(red)","rgb(red)","rgb(blue)","rgb(blue)"]). : room/zone/light "running" reports the live scene or mode currently executing on the affected node(s) via ArtiMONITOR (UDP unicast Node -> Hub, port 1096). Null when nothing is running. For room/zone, the value aggregates across the member lights' nodes (most-common-wins). Hue and standalone slave lights never run scenes/modes and always return null. $ node : {"id":id,"ident":"ident","name":"name","ip":"ip","type":"type","disabled":disabled,"down":down,"fail_count":fail_count} $ slave : {"id":id,"ident":"ident","name":"name","ip":"ip","type":"type","disabled":disabled,"down":down,"fail_count":fail_count} $ relay : {"id":id,"ident":"ident","name":"name","ip":"ip","type":"type","ports":ports,"disabled":disabled,"down":down,"fail_count":fail_count} $ palette : {"palette":{"palette":palette_id,"blend":blend,"scale":scale,"alternate":alternate,"start_index":start_index,"end_index":end_index}} $ custom_palette : {"custom_palette":{"start_color":{"red":red,"green":green,"blue":blue},"end_color":{"red":red,"green":green,"blue":blue},"blend":blend,"scale":scale,"alternate":alternate,"start_index":start_index,"end_index":end_index}} $ advanced_palette : {"advanced_palette":{"color_array":[[pos,red,green,blue],[pos,red,green,blue],...],"blend":blend,"scale":scale,"alternate":alternate,"start_index":start_index,"end_index":end_index}} $ effect_rgb : {"effect_rgb":{"effect":effect_id,"clear":0/1,"step":step,"start_delay":start_delay,"end_delay":end_delay,"red":red,"green":green,"blue":blue}} $ effect_rgbw : {"effect_rgbw":{"effect":effect_id,"clear":0/1,"step":step,"start_delay":start_delay,"end_delay":end_delay,"red":red,"green":green,"blue":blue,"white":white}} Effect IDs : 1=Forward Fill, 2=Reverse Fill, 3=Forward Clear, 4=Reverse Clear, 5=Outward Fill, 6=Center Fill, 7=Outward Clear, 8=Center Clear, 9=Strobe Even, 10=Strobe Vari, 11=Random Fill, 12=Fade In, 13=Fade Out, 14=Forward Gradient In, 15=Reverse Gradient In, 16=Forward Gradient Out, 17=Reverse Gradient Out, 18=Outward Gradient In, 19=Outward Gradient Out, 20=Center Gradient In, 21=Center Gradient Out, 22=Forward Pixel Run, 23=Reverse Pixel Run, 24=Outward Pixel Run, 25=Center Pixel Run, 26=Cross Pixel Run, 27=Smart Forward Fill, 28=Smart Reverse Fill, 29=Smart Alt Forward Fill, 30=Smart Alt Reverse Fill, 31=Smart Forward Clear, 32=Smart Reverse Clear, 33=Smart Alt Forward Clear, 34=Smart Alt Reverse Clear Effect Parameters : effect=Effect ID (1-34), clear=Clear strip first (0/1), step=Step/Repeat/Pixels (0-65000), start_delay=Start delay (0-255), end_delay=End delay/Segments (0-255), Colors (0-255) Effect State Tracking : The Hub API and the Master state tracker derive a light's on/off state and colour from effect commands, based on the colour the effect LEAVES on the strip (black = off). Classification lives in TWO places that must stay in sync with the firmware: - Hub API : api/includes/functions.php -> effect_implied_state() - Master : artiled_state.py -> LIT/DARK/STROBE_EFFECT_IDS + effect_ends_lit() !! When ADDING A NEW EFFECT to the firmware, add its end-state to BOTH. State Semantics : Brightness NEVER RAISES state — a {"brightness":>0} does not turn a light on (state=1 only on an actual draw that lights non-zero pixels; mirrors firmware calculate_power_actual — brightness alone lights nothing). Brightness = 0 LOWERS state to 0 (zero brightness = all pixels dark = off; firmware emits State:0, Master artiled_state.py mirrors it). So dragging a dimmer never toggles power ON, but dimming fully to 0 turns the light OFF. Brightness aggregation differs BY LAYER, by design (do not "unify"): - Master per-node (aggregate_brightness): keeps the last-set brightness even while off (slider memory); reads 0 only when every slave is explicitly 0. - Hub /room /zone (aggregate_lights_for): averages only lights that are ON (state=1); 0 when the room/zone is off. $ notification : {"id":id, "priority":priority,"ident":"ident","name":"name","datetime":"datetime","text":"text"} $ holiday : {"id":id,"name":"name","start_date":"YYYY-MM-DD","end_date":"YYYY-MM-DD","recurring":0/1,"profile_id":id|null,"end_profile_id":id|null,"start_time":"HH:MM:SS"|null,"end_time":"HH:MM:SS"|null,"deactivate_start_at_end":0/1,"last_start_fired":"datetime"|null,"last_end_fired":"datetime"|null} $ event : {"id":id,"name":"name","type":type,"enable":0/1,"icon":"icon","date":"YYYY-MM-DD"|"","time":"HH:MM"|"","sun":"SunRise/SunSet"|"","sun_offset":minutes,"days":["Monday", ...],"months":["January", ...],"seasons":["Winter", ...],"profiles":["profile_name", ...],"daylight":"Daylight/Darkness"|"","randomize":0/1,"randomize_minutes":minutes,"scene_id":id,"scene_selection":0-3,"room_id":id,"zone_id":id,"light_id":id,"flow_id":id,"profile_id":id,"profile_action":"activate/deactivate","control_node_id":id,"control_action":"action","control_target":id,"last_fired":"datetime"|null} Event Types : 1=Scene, 2=HTTP, 3=Code, 4=Flow, 5=Profile, 6=Control Event Selection : scene_selection -> 0=House, 1=Room (room_id), 2=Zone (zone_id), 3=Light (light_id) Event Fields : "date" empty means it repeats; a date in the PAST is disabled by the Hub the next time it runs, because it can never fire again. "time" empty AND no "sun" means it fires at midnight. "days"/"months"/"seasons" are AND filters - empty means "no restriction". "profiles" is a GATE holding profile NAMES, not ids: the event only fires while at least one of them is active. Empty means "always". "sun_offset" is minutes, negative for before. ⚠ "url", "data" and "code" are NOT returned. An HTTP event's configuration can hold credentials, and a Code event's body is a python program the Hub runs - neither belongs in a list of what is scheduled. $ magic : {"type":"wakeup","id":id,"name":"name","enable":0/1,"icon":"icon","wake_time":"HH:MM","start_time":"HH:MM","duration":minutes,"days":["Monday", ...],"start_date":"YYYY-MM-DD"|null,"end_date":"YYYY-MM-DD"|null,"selection":0-3,"room_id":id,"zone_id":id,"light_id":id,"channel":channel,"curve":0-10,"brightness":0/1,"brightness_start":0-255,"brightness_end":0-255,"color_mode":"white/rgb/rgbw/hsv/palette","color_start":{...},"color_end":{...},"running":0/1,"next_run":"YYYY-MM-DD HH:MM"|"","last_fired":"datetime"|null,"last_result":"text"|null} Magic Types : "wakeup", "sleep", "daylight" Magic Selection : 0=House, 1=Room (room_id), 2=Zone (zone_id), 3=Light (light_id) Magic Colors : white {"temperature":0-255,"intensity":0-255} rgb {"red","green","blue"} 0-255 rgbw {"red","green","blue","white"} 0-255 hsv {"hue","saturation","value"} 0-255 palette [[position,r,g,b], ...] $ capabilities : {"dim":1/0,"colors":1/0,"color_profile":"w/rgb/rgbw","segments":0-16} $ running : {"running":[{"ident":"ident","node":"node_name","type":"scene/mode/idle","name":"scene_or_mode_name","image_url":"/images/scenes/...","light_id":N|null,"room_id":N|null,"zone_id":N|null,"last_seen_ago_sec":N,"light_ids":[N,...],"room_ids":[N,...],"zone_ids":[N,...]},...],"count":N,"scenes_running":["name",...],"modes_running":["name",...],"fires":[{fire}],"fires_count":N} : Live cross-node ArtiMONITOR snapshot. Per-node entries include idle nodes that are heartbeating; absence from the list means the node has no heartbeat (silent/offline). last_seen_ago_sec = seconds since the last heartbeat (heartbeat interval is 15s, key TTL is 45s). Per-node "name" is the canonical Hub display name when a matching Hub scene/mode exists, else the Node-reported name verbatim. : "scenes_running" / "modes_running" are de-duplicated Hub display name arrays — useful for "is X playing anywhere?" checks. Same canonical-name resolution as per-node "name". : "fires" is the Hub-side per-fire view (added 1.45). One entry per Hub fire still active — see Data Structure 'fire'. Use this for scope-aware UIs (e.g. show "Music in zone Hifi" as a tile with a Stop button that targets only that zone) and for differentiating concurrent fires of the same scene/mode into different scopes. Per-node "running" is still present for backwards compatibility and for entries no Hub fire claims (Locally-started scenes/modes, or fires the Hub doesn't know about). $ fire : {"uuid":"","type":"scene/mode","hub_id":N,"name":"hub_display_name","broadcast_name":"node_side_name","image_url":"/images/scenes/...","scope_type":"house/room/zone/light","scope_id":N,"scope_label":"","target_nodes":["ident",...] OR ["*"],"confirming_nodes":["ident",...],"confirming_count":N,"confirmed":0/1,"fired_at":epoch,"last_seen":epoch} : One entry per active Hub fire (added 1.45). Lifecycle: - written on /scene or /mode with running:1 - cleared on /scene or /mode with running:0 (matching scope) - cleared automatically when a newer fire fully supersedes its targets - auto-expires ~10s after the last confirming heartbeat, or ~60s from fired_at if never confirmed (also expires by record TTL). : scope_type forces 'house' when the scene/mode uses LAN broadcast regardless of the room/zone the Hub was asked to fire into, because broadcast hits every node on the segment. : target_nodes is the unicast target set, or ["*"] for broadcast fires. confirming_nodes is the subset currently reporting the matching broadcast_name in the cross-node monitor stream. : Stable order: oldest fire first. ============ | Commands | ============ ----------------- | Alive Command | ----------------- > /Alive : No Data -> Returns Hub Name and Ident -> See Data Structure 'name' and 'ident' ----------------- | Login Command | ----------------- > /login : Data -> username and password -> Returns Token -> User Authenticates once with login screen ----------------- | Info Commands | ----------------- > /version : No Data -> Returns Hub Version -> See Data Structure 'version' ---------------- | Get Commands | ---------------- > /house : Empty Data {} -> Returns House -> {"house":{house}} See Data Structure 'house' > /rooms : No Data or {"room":room_id} or {"room":"room_name"} and {"order":"id"} or {"order":"name"} or {"select":"visible/invisible/all"} -> Returns Rooms -> {"rooms":[{room1, room2, ....}]} See Data Structure 'room' > /zones : No Data or {"zone":zone_id} or {"zone":"zone_name"} or {"room":room_id} or {"room":"room_name"} or {"select":"visible/invisible/all"} and {"order":"id"} or {"order":"name"} -> Returns Zones -> {"zones":[{zone1, zone2, ....}]} See Data Structure 'zone' > /scenes : No Data or {"zone":zone_id} or {"zone":"zone_name"} or {"room":room_id} or {"room":"room_name"} or {"select":"visible/invisible/all"} and {"order":"id"} or {"order":"name"} -> Returns Scenes -> {"scenes":[{scene1, scene2, ....}]} See Data Structure 'scene' > /custom_scenes : No Data -> Returns Custom Scenes (scene_type 12 only), by name -> {"scenes":[{scene1, scene2, ....}]} : {"custom_scene":scene_id} -> Returns that one custom scene WITH its members, as a list of one : See Data Structure 'custom_scene'. A scene_id of any other type returns error 4 "Scene Not Found" > /modes : No Data or {"zone":zone_id} or {"zone":"zone_name"} or {"room":room_id} or {"room":"room_name"} and {"order":"id"} or {"order":"name"} -> Returns Modes -> {"modes":[{mode1, mode2, ....}]} See Data Structure 'mode' > /lights : No Data or {"zone":zone_id} or {"zone":"zone_name"} or {"room":room_id} or {"room":"room_name"} or {"light":light_id} or {"light":"light_name"} -> Returns Lights -> {"lights":[{light1, light2, ....}]} See Data Structure 'light' > /notifications : No Data or {"order":id} or {"order":"date"}, {"limit":limit} -> Returns Notifications -> {"notifications":[{notification1, notification2, ....}]} See Data Structure 'notification' > /holidays : Empty Data {} -> Returns Holidays, earliest start first -> {"holidays":[{holiday1, holiday2, ....}]} See Data Structure 'holiday' > /events : Empty Data {} -> Returns Events, by name -> {"events":[{event1, event2, ....}]} See Data Structure 'event' > /magics : Empty Data {} or {"type":"wakeup"} -> Returns Magic entries -> {"magic":[{magic1, magic2, ....}]} See Data Structure 'magic' > /running : No Data -> Returns cross-node ArtiMONITOR snapshot of currently-running scenes and modes -> See Data Structure 'running' > /nodes : No Data -> Returns Nodes -> {"nodes":[{node1, node2, ....}]} See Data Structure 'node' > /slaves : No Data -> Returns Slaves -> {"slaves":[{slave1, slave2, ....}]} See Data Structure 'slave' > /relays : No Data -> Returns Relays -> {"relays":[{relay1, relay2, ....}]} See Data Structure 'relay' ! /strips : ! /segments : ---------------- | Set Commands | ---------------- > /house : Data {"channel":channel},{"state":state},{"brightness":brightness},{"color_w"},{"color_rgb"},{"color_rgbw"},{"color_hsv"},{"palette*"},{"effect_rgb"},{"effect_rgbw"},{"brightness_fade"} -> Sets House -> Returns Response See Data structure 'response' > /room : Data {"room":room_id} or {"room":"room_name"} and {"channel":channel},{"state":state},{"brightness":brightness},{"color_w"},{"color_rgb"},{"color_rgbw"},{"color_hsv"},{"palette*"},{"effect_rgb"},{"effect_rgbw"},{"brightness_fade"} -> Sets Room -> Returns Response See Data structure 'response' > /zone : Data {"zone":zone_id} or {"zone":"zone_name"} and {"channel":channel},{"state":state},{"brightness":brightness},{"color_w"},{"color_rgb"},{"color_rgbw"},{"color_hsv"},{"palette*"},{"effect_rgb"},{"effect_rgbw"},{"brightness_fade"} -> Sets Zone -> Returns Response See Data structure 'response' > /light : Data {"light":light_id} or {"light":"light_name"} and {"channel":channel},{"state":state},{"clear":clear},{"brightness":brightness},{"color_w"},{"color_rgb"},{"color_rgbw"},{"color_hsv"},{"palette*"},{"effect_rgb"},{"effect_rgbw"},{"brightness_fade"},{"segment"} -> Sets Light -> Returns Response See Data structure 'response' : You can add {"hue":1} if you also want to control added or associated Philips Hue Lights (Please note you cannot update them as often as ArtiLED lights). : ⚠ "state" semantics: state=1 sends a RESTORE to each light (wake + reload the controller's last SAVED output), state=0 sends SLEEP. Restore on a light that is already lit reloads the saved frame — which may be the off/dark frame — and visibly kills its output. Therefore: NEVER combine {"state"} with {"brightness"} or a color field. Send brightness/color alone — color commands turn lights on by themselves. Use {"state"} alone, and only for plain on/off. ------------------ | Scene Commands | ------------------ > /scene : Data {"scene":scene_id} or {"scene":"scene_name"} and {"room":room_id} or {"room":"room_name"} or {"zone":zone_id} or {"zone":"zone_name"} or {"light":light_id} or {"light":"light_name"} and optional {"running":0/1} -> Starts (default, or running:1) or Stops (running:0) the scene in the selected scope -> Returns Response See Data structure 'response' : EXACTLY ONE of "room"/"zone"/"light" — the "or" above is exclusive. Naming two is refused (error 5) : A name that does not resolve is REFUSED, never widened to house-wide. A NUMERIC "room":0 is the house-wide shortcut : "running":0 stops the named scene across the selected scope. The stop dispatch mirrors the start dispatch path (UDP unicast / TCP unicast / UDP broadcast per the scene's advanced flags), changing the protocol command to "Stop Scene" (UDP command code 2, TCP "Stop Scene "). Backwards-compatible: callers that omit "running" still get the previous start-only behavior. > /custom_scene : Data {"action":"add"} and fields -> Creates a custom scene -> Returns {"id":new_id} See Data structure 'response' : Data {"custom_scene":scene_id},{"action":"edit"} and fields -> Updates it -> Returns {"id":id} : Data {"custom_scene":scene_id},{"action":"delete"} -> Deletes it -> Returns {"id":id} : Fields: "name","image","members",["rooms"],["zones"],["scene_app_color"],["scene_sort"],["scene_stop_running_scenes"],["scene_stop_running_modes"] : "rooms"/"zones" omitted are derived from the members; sent empty means offered nowhere : Custom scenes only (scene_type 12). Any other scene_id returns error 4 "Scene Not Found" : A delete refused because the scene is in use returns {"uses":[...]} ----------------- | Mode Commands | ----------------- > /mode : Data {"mode":mode_id} or {"mode":"mode_name"} and {"room":room_id} or {"room":"room_name"} or {"zone":zone_id} or {"zone":"zone_name"} or {"light":light_id} or {"light":"light_name"} and optional {"running":0/1} -> Starts (default, or running:1) or Stops (running:0) the mode in the selected scope -> Returns Response See Data structure 'response' : EXACTLY ONE of "room"/"zone"/"light" — the "or" above is exclusive. Naming two is refused (error 5) : A name that does not resolve is REFUSED, never widened to house-wide. A NUMERIC "room":0 is the house-wide shortcut : "running":0 stops the named mode across the selected scope. Same dispatch semantics as /scene above (command code 2 / "Stop Mode "). ----------------- | Flow Commands | ----------------- > /flow : Data {"flow":flow_id} or {"flow":"flow_name"} and {"action":"start"} or {"action":"stop"} -> Starts or stops flow -> Returns Response See Data structure 'response' > /flows : No Data -> Returns Flows -> {"flows":[{flow1, flow2, ....}]} See Data Structure 'flow' $ flow : {"id":id,"name":"name","icon":"icon","enable":0/1,"running":true/false,"last_run":"datetime"} -------------------- | Profile Commands | -------------------- > /profile : Data {"profile":profile_id} or {"profile":"profile_name"} and {"action":"activate"} or {"action":"deactivate"} and optional {"source":"trigger"} -> Activates or deactivates profile -> Returns Response See Data structure 'response' > /profiles : No Data -> Returns Profiles -> {"profiles":[{profile1, profile2, ....}]} See Data Structure 'profile' $ profile : {"id":id,"name":"name","icon":"icon","active":0/1,"protected":0/1,"role_id":role_id} ------------------------- | Notification Commands | ------------------------- > /notification : Data {"notification":notification_id} or {"notifications":[id, id, ....]} -> Deletes one or more notifications -> Returns {"deleted":deleted,"failed":0,"requested":requested,"mode":"ids"} See Data structure 'response' : Data {"older_than_days":days} -> Deletes every notification older than that many days -> Returns {"deleted":deleted,"failed":0,"requested":deleted,"mode":"older_than_days","days":days} : Data {"delete_all":"ALL"} -> Deletes every notification -> Returns {"deleted":deleted,"failed":0,"requested":deleted,"mode":"all"} "notification" id, 1..2147483647 "notifications" array of ids, max 500 "older_than_days" 1..3650 "delete_all" "ALL" > /notifications : See Get Commands - reading the list is a Get. ---------------------- | Holiday Commands | ---------------------- > /holiday : Data {"action":"add"} and fields -> Creates a holiday -> Returns {"id":new_id} See Data structure 'response' : Data {"holiday":holiday_id},{"action":"edit"} and fields -> Updates it -> Returns {"id":id} : Data {"holiday":holiday_id},{"action":"delete"} -> Deletes it -> Returns {"id":id,"deleted":0/1} : Fields for add and edit: "name" required, 1..100 characters "start_date" required, "YYYY-MM-DD", a real calendar date "end_date" required, "YYYY-MM-DD", NOT before start_date "recurring" optional 0/1, default 0 "profile_id" optional, activated at the start; 0/""/null = none "end_profile_id" optional, activated at the end; 0/""/null = none "start_time" optional "HH:MM" or "HH:MM:SS"; none = start of day "end_time" optional "HH:MM" or "HH:MM:SS" "deactivate_start_at_end" optional 0/1, default 0 - turn the start profile off again when the holiday ends -------------------- | Event Commands | -------------------- > /event : Data {"action":"add"} and fields -> Creates an event -> Returns {"id":new_id} See Data structure 'response' : Data {"event":event_id},{"action":"edit"} and fields -> Updates it -> Returns {"id":id} : Data {"event":event_id},{"action":"delete"} -> Deletes it -> Returns {"id":id,"deleted":0/1} : Data {"event":event_id},{"action":"enable"} or {"action":"disable"} -> Returns {"id":id,"enable":0/1} : Data {"event":event_id},{"action":"test"} -> Runs it now -> Returns {"id":id} : add and edit accept "type" 1 (Scene), 4 (Flow) and 5 (Profile) only. Types 2 (HTTP), 3 (Code) and 6 (Control) can be read, enabled, disabled and deleted; edit or test on one returns error 6. : Fields for add and edit: "name" required, 1..50 characters "type" 1=Scene (default), 4=Flow, 5=Profile "enable" optional 0/1, default 1 "icon" optional, Font Awesome classes, max 30 characters, [A-Za-z0-9 _-] only; default "fas fa-couch" "date" optional "YYYY-MM-DD" one-off date; "" = it repeats "time" optional "HH:MM" or "HH:MM:SS" "sun" optional "SunRise" or "SunSet" "sun_offset" optional minutes, one of 0, +/-5, 10, 15, 20, 25, 30, 45, 60, 90, 120 "days" optional ["Monday", ...] or a comma string "months" optional ["January", ...] or a comma string "seasons" optional ["Winter","Spring","Summer","Autumn"] "profiles" optional ["profile_name", ...] - the gate, by NAME "daylight" optional "Daylight" or "Darkness" "randomize" optional 0/1, default 0 "randomize_minutes" optional, one of 5, 10, 15, 30, 45, 60; default 15 type 1 only: "scene_id" required "scene_selection" optional 0=House (default), 1=Room, 2=Zone, 3=Light "room_id" required when scene_selection is 1 "zone_id" required when scene_selection is 2 "light_id" required when scene_selection is 3 type 4 only: "flow_id" required type 5 only: "profile_id" required "profile_action" optional "activate" (default) or "deactivate" : "time" and "sun" are mutually exclusive; neither = midnight. > /magic : Data {"type":"wakeup","action":"add"} and fields -> Creates a wake-up -> Returns {"id":new_id} : Data {"type":"wakeup","magic":id},{"action":"edit"} and fields -> Updates it -> Returns {"id":id} : Data {"type":"wakeup","magic":id},{"action":"delete"} -> Deletes it -> Returns {"id":id,"deleted":0/1} : Data {"type":"wakeup","magic":id},{"action":"enable"} or {"action":"disable"} -> Returns {"id":id,"enable":0/1} : Data {"type":"wakeup","magic":id},{"action":"test"} -> Runs it now -> Returns {"id":id} : Data {"type":"wakeup","magic":id},{"action":"stop"} -> Cancels a running ramp -> Returns {"id":id} > /magics : See Get Commands - reading the list is a Get. > /events : See Get Commands - reading the list is a Get.