FX-Framework API (Version 0)

This is documentation for the current web API used on products based on FX-Framework.

It's a collection of endpoints accumulated over the years in the Web-UI. We're aware this isn't a web API in the modern sense.

Eventually there will be a proper API (version 1) in the future.

This document describes the endpoints available on the Morph 4K. Some (as the websocket interface) might not be available on G.E.M. or Digital products!

Work in progress: Especially the URI based interfaces aren't completely documented yet.

Websocket Interface:

Preface

For easy debugging, we suggest using a command line websocket client like websocat (in conjunction with readline wrapper rlwrap)

Connect to ws://morph4k.local/ws/api with websocket protocol morph4k

rlwrap websocat --protocol morph4k ws://morph4k.local/ws/api --linemode-strip-newlines

Starting/stopping an sdcard "session"

Send a single start_sdcard command.

This will make sure, the sdcard command handling task is running on the ESP.

You will notice a 1 (in case the SD card is mounted) or a 0 is sent by the server side every second.

This can be used for both an alive check and informs about the current sdcard state (mounted/not mounted) At the end of a session, you should send stop_sdcard command, so the ESP can free up memory, if the interface isn't needed.

SD card command structure:

sdcard=<JSON>

Specifically:

sdcard={"a":"<ACTION>","v":["<ARG1>","<ARG2>",....]}

Directory listing reply

Most commands reply with the directory listing reply

{"e":[<ELEMENT1>,<ELEMENT2>,...],"r":"<DIRECTORY_CONTEXT>"}

Each is element is either a file:

{"n":"<NAME>","d":false,"s":<SIZE_IN_BYTES>}

or a directory

{"n":"<NAME>","d":true}

If an error occurs there will be an "error" attribute added to the reply JSON:

"error":"<ERROR>"

Commands:

List

ACTION ARG1
list directory

Reply

Replies with the directory listing reply.

Example

sdcard={"a":"list","v":["/sdcard/presets"]}

Special case for list:


Remove

ACTION ARG1 ARG2 ARG3
remove directory filename recursive

Reply

Replies with the (updated) directory listing reply.

Example

sdcard={"a":"remove","v":["/sdcard/presets","test.ini","-r"]}

Create directory

ACTION ARG1 ARG2 ARG3
mkdir directory dirname ensure_parent_dirs

Reply

Replies with the (updated) directory listing reply.

Example

sdcard={"a":"mkdir","v":["/sdcard/fw","test","-p"]}

Rename / Move file or directory

ACTION ARG1 ARG2 ARG3 ARG4
mv directory filename targetpath overwrite

Reply

Replies with the (updated) directory listing reply.

Example

sdcard={"a":"mv","v":["/sdcard/fw","test","/sdcard/update/testo",""]}

Create preset

ACTION ARG1 ARG2 ARG3 ARG4 ARG5
preset_create directory filename preset_description overwrite create_mask

Reply

Replies with the (updated) directory listing reply.

Example

sdcard={"a":"preset_create","v":["/sdcard/presets","Example preset.ini","This is an example preset\\nline2\\nline3","-o","0x7FFFFFFF"]}

Edit preset

ACTION ARG1 ARG2 ARG3 ARG4 ARG5
preset_create directory filename targetpath overwrite preset_description

Reply

Replies with the (updated) directory listing reply.

Example

sdcard={"a":"edit","v":["/sdcard/presets","test.ini","/sdcard/presets/oest.ini","-o","Created at 2024/08/14 13:45:28 CEST\\n- 1920x1080p input\\n- 4K50 output"]}	

Get file / directory information

ACTION ARG1 ARG2 ARG3
info directory filename include_directory_structure

Reply

Common to all file types:

{"d":{"f":"<FILENAME>","m":<TYPE>},"r":"<DIRECTORY_CONTEXT>"}

With:

Special attributes for a firmware update file (TYPE == -2):
{"d":{"f":"<FILENAME>","m":-2,"s":<FILESIZE_IN_BYTES>,"fw_r":"<RUNNING_FW_VERSION>","fw_rc":"<RUNNING_FW_CHECKSUM>","fw_s":"<THIS_FILE_FW_VERSION>","fw_sc":"<THIS_FILE_FW_CHECKSUM>"},"r":"<DIRECTORY_CONTEXT>"}

or

{"d":{"f":"<FILENAME>","m":-2,"s":<FILESIZE_IN_BYTES>,"fw_r":"<RUNNING_FW_VERSION>","fw_rc":"<RUNNING_FW_CHECKSUM>","fw_e":"<THIS_FILE_ERROR>"},"r":"<DIRECTORY_CONTEXT>"}
Examples
{"d":{"f":"morph4k-update.fx.verified","p":"morph4k-update.fx.verified","d":"__fx_file__","m":-2,"s":4770603,"fw_r":"3.9.46","fw_rc":"7e1b5dca","fw_s":"3.9.46","fw_sc":"7e1b5dca"},"r":"/sdcard/update"}
{"d":{"f":"morph4k-update-T120.fx","p":"morph4k-update-T120.fx","d":"__fx_file__","m":-2,"s":4315835,"fw_r":"3.9.46","fw_rc":"7e1b5dca","fw_e":"invalid_fw_file"},"r":"/sdcard/update"}
Special attributes for a preset file (TYPE > 0):
{"d":{"f":"<FILENAME>","d":"<PRESET_DESCRIPTION>","m":<TYPE>,"s":<FILESIZE_IN_BYTES>},"r":"<DIRECTORY_CONTEXT>"}
Example
{"d":{"f":"trest.ini","p":"trest.ini","d":"Created at 2024/08/14 13:45:28 CEST\n- 1920x1080p input\n- 4K50 output","m":16762751,"s":2716}}
Special attributes for a standard files (TYPE == 0):
{"d":{"f":"<FILENAME>","m":<TYPE>,"s":<FILESIZE_IN_BYTES>},"r":"<DIRECTORY_CONTEXT>"}
Special attributes, if include_directory_structure is set to -f
{"d":{"f":"<FILENAME>","m":<TYPE>,"s":<FILESIZE_IN_BYTES>},"ds":{<DIRECTORY_LIST>}"r":"<DIRECTORY_CONTEXT>"}
Example
{"d":{"f":"test","m":-1},"ds":[{"n":"/update/test"},{"n":"/update"}],"r":"/sdcard/update"}

URI based interfaces:

Preface

If your device has credentials disabled, the --digest --user morph4k:password part can be omitted. Otherwise you have to adjust adjust --user morph4k:password to your settings (Web pass)

Upload to SD card

Request

POST /upload/sdcard/<PATH_ON_SDCARD>

File contents are sent in the POST body without any encoding (plain binary)

Non-existing directories are created if needed, an existing file with the same path is overwritten.

Response

Example

curl --digest --user morph4k:password -X POST --data-binary @result/morph4k-rescue.binc "http://morph4k.local/upload/sdcard/fw/morph4k-rescue.binc"

Apply preset

Request

POST /preset-apply/sdcard/<PATH_ON_SDCARD>

No POST body

Response

Example

curl --digest --user morph4k:password -X POST "http://morph4k.local/preset-apply/sdcard/presets/default.ini"

Get firmware info

Request

GET /firmware-info

Response

JSON providing firmware and runtime information.

TODO

Example

# Request
curl -s --digest --user morph4k:password "http://morph4k.local/firmware-info" | jq .
// Response
{
  "driver_id": "morph4k",
  "console_id": "morph",
  "dev_mode": true,
  "sdcard_mounted": true,
  "standby_mode": false,
  "device": {
    "platform": "individual",
    "uuid": "dbe84bfe-1879-11ef-b754-bbc071fd24a9",
    "datecode": "20240522-202833",
    "iversion": "3.3.0",
    "batch": 200,
    "tier": "Shiny Edition"
  },
  "firmware": {
    "version": "fx-framework 3.9.46",
    "builddate": "(7e1b5dca)",
    "bootloader": "v1",
    "rescue": "9.0.0",
    "channel": "experimental",
    "uri": "http://firmware.pixelfx.co/fwd2.php?n=3&uuid=dbe84bfe-1879-11ef-b754-bbc071fd24a9&cv=3.9.46&pid=morph4k&did=morph4k&ver=%s&l="
  },
  "features": {
    "has_analog_out": false,
    "has_deblur": false,
    "has_gamma.input": true,
    "has_gamma.output": true,
    "has_slotmask": true,
    "has_smoothing": true
  },
  "user_files": {
    "buttons.ini": false,
    "gamma.txt": false,
    "ir.ini": false,
    "modelines.ini": false,
    "presets.ini": true,
    "screensaver.txt": false
  },
  "wifi_status": {
    "state": "WIFI_SERVICE_STA_CONNECTED",
    "ip": "192.168.2.56",
    "channel": 11,
    "ap_ssid": "MORPH 4K",
    "quality": 100,
    "rssi": -49,
    "max_tx_power": 20,
    "sta_ssid": "z2600.f1",
    "bandwidth": "HT40",
    "ips_connected": -1
  },
  "heap_info": {
    "internal": {
      "allocated_blocks": 774,
      "free_blocks": 21,
      "largest_free_block": 31744,
      "minimum_free_bytes": 35088,
      "total_allocated_bytes": 243804,
      "total_blocks": 795,
      "total_free_bytes": 49212
    },
    "total": {
      "allocated_blocks": 1176,
      "free_blocks": 43,
      "largest_free_block": 5242880,
      "minimum_free_bytes": 5253028,
      "total_allocated_bytes": 368308,
      "total_blocks": 1219,
      "total_free_bytes": 5346780
    }
  }
}

OSD remote control

Request

POST /btn/<KEY>/<REPEAT>

With:

Response

Example

# Press OK (no repeat)
curl --digest --user morph4k:password -X POST "http://morph4k.local/btn/o/0"