[{"data":1,"prerenderedAt":1278},["ShallowReactive",2],{"navigation":3,"\u002Fgetting-started\u002Ffirst-run":99,"\u002Fgetting-started\u002Ffirst-run-surround":1273},[4,16,28,55,82],{"title":5,"path":6,"stem":7,"children":8,"icon":10},"Get started","\u002Fgetting-started","1.getting-started\u002F1.index",[9,11],{"title":5,"path":6,"stem":7,"icon":10},"i-lucide-rocket",{"title":12,"path":13,"stem":14,"icon":15},"Your first station run","\u002Fgetting-started\u002Ffirst-run","1.getting-started\u002F2.first-run","i-lucide-play",{"title":17,"path":18,"stem":19,"children":20,"icon":22},"Core concepts","\u002Fcore-concepts","2.core-concepts\u002F1.index",[21,23],{"title":17,"path":18,"stem":19,"icon":22},"i-lucide-boxes",{"title":24,"path":25,"stem":26,"icon":27},"Projects and access","\u002Fcore-concepts\u002Fprojects-and-access","2.core-concepts\u002F2.projects-and-access","i-lucide-users",{"title":29,"path":30,"stem":31,"children":32,"icon":34},"Station runtime","\u002Fstation-runtime","3.station-runtime\u002F1.index",[33,35,40,45,50],{"title":29,"path":30,"stem":31,"icon":34},"i-lucide-circuit-board",{"title":36,"path":37,"stem":38,"icon":39},"Configure a station","\u002Fstation-runtime\u002Fconfiguration","3.station-runtime\u002F2.configuration","i-lucide-file-code-2",{"title":41,"path":42,"stem":43,"icon":44},"Custom stages","\u002Fstation-runtime\u002Fcustom-stages","3.station-runtime\u002F3.custom-stages","i-lucide-puzzle",{"title":46,"path":47,"stem":48,"icon":49},"Credentials and secrets","\u002Fstation-runtime\u002Fcredentials-and-secrets","3.station-runtime\u002F4.credentials-and-secrets","i-lucide-key-round",{"title":51,"path":52,"stem":53,"icon":54},"Multiple lanes","\u002Fstation-runtime\u002Fmultiple-lanes","3.station-runtime\u002F5.multiple-lanes","i-lucide-git-branch",{"title":56,"path":57,"stem":58,"children":59,"icon":61},"Production workflows","\u002Fproduction-workflows","4.production-workflows\u002F1.index",[60,62,67,72,77],{"title":56,"path":57,"stem":58,"icon":61},"i-lucide-factory",{"title":63,"path":64,"stem":65,"icon":66},"Applications and firmware","\u002Fproduction-workflows\u002Ffirmware-images","4.production-workflows\u002F2.firmware-images","i-lucide-cpu",{"title":68,"path":69,"stem":70,"icon":71},"DUT records and IDs","\u002Fproduction-workflows\u002Fdut-records-and-ids","4.production-workflows\u002F3.dut-records-and-ids","i-lucide-scan-barcode",{"title":73,"path":74,"stem":75,"icon":76},"Results and logs","\u002Fproduction-workflows\u002Fresults-and-logs","4.production-workflows\u002F4.results-and-logs","i-lucide-chart-no-axes-combined",{"title":78,"path":79,"stem":80,"icon":81},"Operator kiosk","\u002Fproduction-workflows\u002Foperator-kiosk","4.production-workflows\u002F5.operator-kiosk","i-lucide-monitor",{"title":83,"path":84,"stem":85,"children":86,"icon":88},"Reference","\u002Freference","5.reference\u002F1.index",[87,89,94],{"title":83,"path":84,"stem":85,"icon":88},"i-lucide-book-open",{"title":90,"path":91,"stem":92,"icon":93},"API access","\u002Freference\u002Fapi-access","5.reference\u002F2.api-access","i-lucide-braces",{"title":95,"path":96,"stem":97,"icon":98},"Troubleshooting","\u002Freference\u002Ftroubleshooting","5.reference\u002F3.troubleshooting","i-lucide-life-buoy",{"id":100,"title":12,"body":101,"description":1266,"extension":1267,"links":1268,"meta":1269,"navigation":1270,"path":13,"seo":1271,"stem":14,"__hash__":1272},"docs\u002F1.getting-started\u002F2.first-run.md",{"type":102,"value":103,"toc":1257},"minimark",[104,122,141,146,181,188,194,197,304,314,318,328,348,354,357,394,402,406,409,434,444,461,498,502,516,714,720,724,741,747,764,1068,1080,1084,1087,1096,1099,1107,1116,1119,1128,1159,1163,1241,1253],[105,106,107,108,112,113,117,118,121],"p",{},"This walkthrough takes you from an empty project to a completed test. It uses the station's built-in ",[109,110,111],"code",{},"print"," stage and one small custom stage to prove that a station can read a value from ",[114,115,116],"strong",{},"Station secrets",". Use a disposable value such as ",[109,119,120],{},"Hello from HTF"," for this exercise. Stage logs appear in the station terminal and in HTF.",[105,123,124,125,132,133,136,137,140],{},"You need an HTF organisation and project, permission to create stations, ",[126,127,131],"a",{"href":128,"rel":129},"https:\u002F\u002Fdocs.astral.sh\u002Fuv\u002F",[130],"nofollow","uv",", and a macOS or Linux machine that can reach ",[109,134,135],{},"app.embeint-htf.com"," and ",[109,138,139],{},"mqtt.app.embeint-htf.com:8883",". The commands below use a shell on that station machine.",[142,143,145],"h2",{"id":144},"_1-create-the-station","1. Create the station",[147,148,149,162,172],"ol",{},[150,151,152,153,157,158,161],"li",{},"Sign in at ",[126,154,135],{"href":155,"rel":156},"https:\u002F\u002Fapp.embeint-htf.com",[130],", open your project, and select ",[114,159,160],{},"Stations",".",[150,163,164,165,168,169,161],{},"Select ",[114,166,167],{},"Add station",". Choose the project and group, and name the station, for example ",[109,170,171],{},"Quickstart bench",[150,173,164,174,177,178,161],{},[114,175,176],{},"Continue"," to reach ",[114,179,180],{},"Station runtime YAML",[105,182,183],{},[184,185],"img",{"alt":186,"src":187},"New station form with project, group, and station name","\u002Fimages\u002Fproduct\u002F03-add-station.png",[105,189,190],{},[184,191],{"alt":192,"src":193},"Runtime YAML step of the new station form","\u002Fimages\u002Fproduct\u002F03b-runtime-yaml.png",[105,195,196],{},"Replace the editor contents with this complete first-run plan. The first stage prints the DUT ID. The second uses the small Python stage added in step 5.",[198,199,204],"pre",{"className":200,"code":201,"language":202,"meta":203,"style":203},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","stages:\n  - name: Print DUT ID\n    kind: print\n    message: 'Testing ${dut_id}'\n    wait_seconds: 0\n\n  - name: Print demo secret\n    kind: print_demo_secret\n","yaml","",[109,205,206,219,235,246,263,275,282,294],{"__ignoreMap":203},[207,208,211,215],"span",{"class":209,"line":210},"line",1,[207,212,214],{"class":213},"swJcz","stages",[207,216,218],{"class":217},"sMK4o",":\n",[207,220,222,225,228,231],{"class":209,"line":221},2,[207,223,224],{"class":217},"  -",[207,226,227],{"class":213}," name",[207,229,230],{"class":217},":",[207,232,234],{"class":233},"sfazB"," Print DUT ID\n",[207,236,238,241,243],{"class":209,"line":237},3,[207,239,240],{"class":213},"    kind",[207,242,230],{"class":217},[207,244,245],{"class":233}," print\n",[207,247,249,252,254,257,260],{"class":209,"line":248},4,[207,250,251],{"class":213},"    message",[207,253,230],{"class":217},[207,255,256],{"class":217}," '",[207,258,259],{"class":233},"Testing ${dut_id}",[207,261,262],{"class":217},"'\n",[207,264,266,269,271],{"class":209,"line":265},5,[207,267,268],{"class":213},"    wait_seconds",[207,270,230],{"class":217},[207,272,274],{"class":273},"sbssI"," 0\n",[207,276,278],{"class":209,"line":277},6,[207,279,281],{"emptyLinePlaceholder":280},true,"\n",[207,283,285,287,289,291],{"class":209,"line":284},7,[207,286,224],{"class":217},[207,288,227],{"class":213},[207,290,230],{"class":217},[207,292,293],{"class":233}," Print demo secret\n",[207,295,297,299,301],{"class":209,"line":296},8,[207,298,240],{"class":213},[207,300,230],{"class":217},[207,302,303],{"class":233}," print_demo_secret\n",[105,305,164,306,309,310,313],{},[114,307,308],{},"Create",". The ",[114,311,312],{},"Station keys"," dialog opens after creation.",[142,315,317],{"id":316},"_2-save-the-one-time-keys-and-certificate","2. Save the one-time keys and certificate",[105,319,320,321,323,324,327],{},"In ",[114,322,312],{},", select ",[114,325,326],{},"Copy .env"," and save the copied text in a temporary protected location. It contains the station ID, organisation ID, MQTT username and password, API key, server address, and certificate paths. The API key and MQTT password are shown only once.",[105,329,330,331,323,334,337,338,136,341,344,345,161],{},"In the same dialog, under ",[114,332,333],{},"Station TLS certificates",[114,335,336],{},"Issue and download",". Save the ZIP bundle, then close the dialog. The bundle contains ",[109,339,340],{},"client.pem",[109,342,343],{},"client.key","; the private key is also delivered only once. If you need to find the certificate controls later, open the station and select ",[114,346,347],{},"Station settings",[105,349,350],{},[184,351],{"alt":352,"src":353},"Station settings showing the certificate issue and download action","\u002Fimages\u002Fproduct\u002F13-station-certificates.png",[105,355,356],{},"Keep the bundle outside the Git checkout. For example, after replacing the filename below with the file you downloaded:",[198,358,362],{"className":359,"code":360,"language":361,"meta":203,"style":203},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","install_dir=\"$HOME\u002F.config\u002Fembeint-htf\u002Fquickstart\"\nbundle=\"$HOME\u002FDownloads\u002FPASTE_DOWNLOADED_FILENAME.zip\"\nmkdir -p \"$install_dir\"\nchmod 700 \"$install_dir\"\nunzip \"$bundle\" -d \"$install_dir\"\nchmod 600 \"$install_dir\u002Fclient.key\"\n","sh",[109,363,364,369,374,379,384,389],{"__ignoreMap":203},[207,365,366],{"class":209,"line":210},[207,367,368],{},"install_dir=\"$HOME\u002F.config\u002Fembeint-htf\u002Fquickstart\"\n",[207,370,371],{"class":209,"line":221},[207,372,373],{},"bundle=\"$HOME\u002FDownloads\u002FPASTE_DOWNLOADED_FILENAME.zip\"\n",[207,375,376],{"class":209,"line":237},[207,377,378],{},"mkdir -p \"$install_dir\"\n",[207,380,381],{"class":209,"line":248},[207,382,383],{},"chmod 700 \"$install_dir\"\n",[207,385,386],{"class":209,"line":265},[207,387,388],{},"unzip \"$bundle\" -d \"$install_dir\"\n",[207,390,391],{"class":209,"line":277},[207,392,393],{},"chmod 600 \"$install_dir\u002Fclient.key\"\n",[105,395,396,397,136,399,401],{},"Check that ",[109,398,340],{},[109,400,343],{}," are in that directory. The certificate proves this station's identity to the public MQTT broker; the MQTT username and password are still required.",[142,403,405],{"id":404},"_3-install-the-station-sample-and-its-credentials","3. Install the station sample and its credentials",[105,407,408],{},"Clone the station repository and install its dependencies:",[198,410,412],{"className":359,"code":411,"language":361,"meta":203,"style":203},"git clone https:\u002F\u002Fgithub.com\u002FEmbeint\u002Fembeint-htf-station.git\ncd embeint-htf-station\nuv sync --all-groups\ncp samples\u002Fbasic-station\u002F.env.example samples\u002Fbasic-station\u002F.env\n",[109,413,414,419,424,429],{"__ignoreMap":203},[207,415,416],{"class":209,"line":210},[207,417,418],{},"git clone https:\u002F\u002Fgithub.com\u002FEmbeint\u002Fembeint-htf-station.git\n",[207,420,421],{"class":209,"line":221},[207,422,423],{},"cd embeint-htf-station\n",[207,425,426],{"class":209,"line":237},[207,427,428],{},"uv sync --all-groups\n",[207,430,431],{"class":209,"line":248},[207,432,433],{},"cp samples\u002Fbasic-station\u002F.env.example samples\u002Fbasic-station\u002F.env\n",[105,435,436,437,440,441,443],{},"Open ",[109,438,439],{},"samples\u002Fbasic-station\u002F.env"," and replace its example values with the text from ",[114,442,326],{},". Change these two lines to the private certificate location used above:",[198,445,449],{"className":446,"code":447,"language":448,"meta":203,"style":203},"language-dotenv shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","HTF_MQTT_CLIENT_CERT=~\u002F.config\u002Fembeint-htf\u002Fquickstart\u002Fclient.pem\nHTF_MQTT_CLIENT_KEY=~\u002F.config\u002Fembeint-htf\u002Fquickstart\u002Fclient.key\n","dotenv",[109,450,451,456],{"__ignoreMap":203},[207,452,453],{"class":209,"line":210},[207,454,455],{},"HTF_MQTT_CLIENT_CERT=~\u002F.config\u002Fembeint-htf\u002Fquickstart\u002Fclient.pem\n",[207,457,458],{"class":209,"line":221},[207,459,460],{},"HTF_MQTT_CLIENT_KEY=~\u002F.config\u002Fembeint-htf\u002Fquickstart\u002Fclient.key\n",[105,462,463,464,467,468,467,471,467,474,477,478,481,482,467,485,467,488,477,491,309,494,497],{},"Confirm the file also has non-empty ",[109,465,466],{},"HTF_ORG_ID",", ",[109,469,470],{},"HTF_STATION_ID",[109,472,473],{},"HTF_MQTT_USERNAME",[109,475,476],{},"HTF_MQTT_PASSWORD",", and ",[109,479,480],{},"HTF_API_KEY",". The production values should be ",[109,483,484],{},"HTF_MQTT_TRANSPORT=mtls",[109,486,487],{},"HTF_MQTT_HOST=mqtt.app.embeint-htf.com",[109,489,490],{},"HTF_MQTT_PORT=8883",[109,492,493],{},"HTF_API_BASE_URL=https:\u002F\u002Fapp.embeint-htf.com",[109,495,496],{},".env"," file is ignored by the station repository; do not add it or the certificate bundle to Git.",[142,499,501],{"id":500},"_4-copy-the-local-station-yaml","4. Copy the local station YAML",[105,503,504,505,508,509,512,513,515],{},"Replace ",[109,506,507],{},"samples\u002Fbasic-station\u002Fconfig.yaml"," with the following complete example. This ",[114,510,511],{},"local file"," tells the Python process how to connect and identifies the station. The ",[109,514,214],{}," section is a fallback for a direct run; HTF supplies the saved runtime plan from step 1 when the station connects.",[198,517,519],{"className":200,"code":518,"language":202,"meta":203,"style":203},"mqtt:\n  transport: mtls\n  host: mqtt.app.embeint-htf.com\n  port: 8883\n  client_cert: ${HTF_MQTT_CLIENT_CERT}\n  client_key: ${HTF_MQTT_CLIENT_KEY}\n\nserver:\n  api_base_url: https:\u002F\u002Fapp.embeint-htf.com\n\nstation:\n  org_id: ${HTF_ORG_ID}\n  station_id: ${HTF_STATION_ID}\n\nstages:\n  - name: Print DUT ID\n    kind: print\n    message: 'Testing ${dut_id}'\n    wait_seconds: 0\n\n  - name: Print demo secret\n    kind: print_demo_secret\n",[109,520,521,528,538,548,558,568,578,582,589,600,605,613,624,635,640,647,658,667,680,689,694,705],{"__ignoreMap":203},[207,522,523,526],{"class":209,"line":210},[207,524,525],{"class":213},"mqtt",[207,527,218],{"class":217},[207,529,530,533,535],{"class":209,"line":221},[207,531,532],{"class":213},"  transport",[207,534,230],{"class":217},[207,536,537],{"class":233}," mtls\n",[207,539,540,543,545],{"class":209,"line":237},[207,541,542],{"class":213},"  host",[207,544,230],{"class":217},[207,546,547],{"class":233}," mqtt.app.embeint-htf.com\n",[207,549,550,553,555],{"class":209,"line":248},[207,551,552],{"class":213},"  port",[207,554,230],{"class":217},[207,556,557],{"class":273}," 8883\n",[207,559,560,563,565],{"class":209,"line":265},[207,561,562],{"class":213},"  client_cert",[207,564,230],{"class":217},[207,566,567],{"class":233}," ${HTF_MQTT_CLIENT_CERT}\n",[207,569,570,573,575],{"class":209,"line":277},[207,571,572],{"class":213},"  client_key",[207,574,230],{"class":217},[207,576,577],{"class":233}," ${HTF_MQTT_CLIENT_KEY}\n",[207,579,580],{"class":209,"line":284},[207,581,281],{"emptyLinePlaceholder":280},[207,583,584,587],{"class":209,"line":296},[207,585,586],{"class":213},"server",[207,588,218],{"class":217},[207,590,592,595,597],{"class":209,"line":591},9,[207,593,594],{"class":213},"  api_base_url",[207,596,230],{"class":217},[207,598,599],{"class":233}," https:\u002F\u002Fapp.embeint-htf.com\n",[207,601,603],{"class":209,"line":602},10,[207,604,281],{"emptyLinePlaceholder":280},[207,606,608,611],{"class":209,"line":607},11,[207,609,610],{"class":213},"station",[207,612,218],{"class":217},[207,614,616,619,621],{"class":209,"line":615},12,[207,617,618],{"class":213},"  org_id",[207,620,230],{"class":217},[207,622,623],{"class":233}," ${HTF_ORG_ID}\n",[207,625,627,630,632],{"class":209,"line":626},13,[207,628,629],{"class":213},"  station_id",[207,631,230],{"class":217},[207,633,634],{"class":233}," ${HTF_STATION_ID}\n",[207,636,638],{"class":209,"line":637},14,[207,639,281],{"emptyLinePlaceholder":280},[207,641,643,645],{"class":209,"line":642},15,[207,644,214],{"class":213},[207,646,218],{"class":217},[207,648,650,652,654,656],{"class":209,"line":649},16,[207,651,224],{"class":217},[207,653,227],{"class":213},[207,655,230],{"class":217},[207,657,234],{"class":233},[207,659,661,663,665],{"class":209,"line":660},17,[207,662,240],{"class":213},[207,664,230],{"class":217},[207,666,245],{"class":233},[207,668,670,672,674,676,678],{"class":209,"line":669},18,[207,671,251],{"class":213},[207,673,230],{"class":217},[207,675,256],{"class":217},[207,677,259],{"class":233},[207,679,262],{"class":217},[207,681,683,685,687],{"class":209,"line":682},19,[207,684,268],{"class":213},[207,686,230],{"class":217},[207,688,274],{"class":273},[207,690,692],{"class":209,"line":691},20,[207,693,281],{"emptyLinePlaceholder":280},[207,695,697,699,701,703],{"class":209,"line":696},21,[207,698,224],{"class":217},[207,700,227],{"class":213},[207,702,230],{"class":217},[207,704,293],{"class":233},[207,706,708,710,712],{"class":209,"line":707},22,[207,709,240],{"class":213},[207,711,230],{"class":217},[207,713,303],{"class":233},[105,715,716,717,719],{},"The certificate paths and identity values are filled from ",[109,718,496],{}," when the sample starts. For a different HTF deployment, use its API and MQTT endpoints.",[142,721,723],{"id":722},"_5-add-a-demo-secret-and-the-stage-that-reads-it","5. Add a demo secret and the stage that reads it",[105,725,726,727,729,730,733,734,736,737,740],{},"Open the station in HTF and select ",[114,728,116],{},". Enter ",[109,731,732],{},"DEMO_MESSAGE"," as the name and ",[109,735,120],{}," as the value, then select ",[114,738,739],{},"Save secret",". The value is shown only during entry. The station fetches current secrets when it starts or reconnects, so restart it after changing a value.",[105,742,743],{},[184,744],{"alt":745,"src":746},"Station secrets form with the DEMO_MESSAGE name","\u002Fimages\u002Fproduct\u002F12-station-secrets.png",[105,748,749,750,752,753,756,757,760,761,763],{},"The built-in ",[109,751,111],{}," stage does not substitute station secrets into YAML. To prove the value reached the station, replace ",[109,754,755],{},"samples\u002Fbasic-station\u002Fmain.py"," with this small sample. It registers ",[109,758,759],{},"print_demo_secret",", reads ",[109,762,732],{}," through the stage context, and prints it using the normal stage logger.",[198,765,769],{"className":766,"code":767,"language":768,"meta":203,"style":203},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","from __future__ import annotations\n\nimport argparse\nimport asyncio\nfrom datetime import UTC, datetime\nfrom pathlib import Path\n\nfrom embeint_htf_station.config import StageSettings, load_settings_from_yaml\nfrom embeint_htf_station.stages.base import StageContext, StageLogger, StageResult\nfrom embeint_htf_station.stations import BasicStation\n\n\nclass PrintDemoSecretStage:\n    def __init__(self, settings: StageSettings) -> None:\n        self.settings = settings\n\n    async def run(self, logger: StageLogger, context: StageContext) -> StageResult:\n        started_at = datetime.now(UTC)\n        value = context.require_secret(\"DEMO_MESSAGE\")\n        await logger.log(\"info\", f\"Demo secret: {value}\")\n        return StageResult(\n            name=self.settings.name,\n            outcome=\"passed\",\n            started_at=started_at,\n            finished_at=datetime.now(UTC),\n        )\n\n\ndef main() -> None:\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"dut_id\", nargs=\"?\")\n    parser.add_argument(\"--listen\", action=\"store_true\")\n    parser.add_argument(\n        \"--config\",\n        type=Path,\n        default=Path(__file__).with_name(\"config.yaml\"),\n    )\n    args = parser.parse_args()\n\n    settings = load_settings_from_yaml(args.config)\n    station = BasicStation(\n        settings,\n        stage_factories={\"print_demo_secret\": PrintDemoSecretStage},\n    )\n    if args.listen:\n        asyncio.run(station.serve_forever())\n    else:\n        if not args.dut_id:\n            parser.error(\"Enter a DUT ID, or use --listen\")\n        result = asyncio.run(station.run_once(args.dut_id))\n        print(f\"test finished: {result.outcome}\")\n\n\nif __name__ == \"__main__\":\n    main()\n","python",[109,770,771,776,780,785,790,795,800,804,809,814,819,823,827,832,837,842,846,851,856,861,866,871,876,882,888,894,900,905,910,916,922,928,934,940,946,952,958,964,970,975,981,987,993,999,1004,1010,1016,1022,1028,1034,1040,1046,1051,1056,1062],{"__ignoreMap":203},[207,772,773],{"class":209,"line":210},[207,774,775],{},"from __future__ import annotations\n",[207,777,778],{"class":209,"line":221},[207,779,281],{"emptyLinePlaceholder":280},[207,781,782],{"class":209,"line":237},[207,783,784],{},"import argparse\n",[207,786,787],{"class":209,"line":248},[207,788,789],{},"import asyncio\n",[207,791,792],{"class":209,"line":265},[207,793,794],{},"from datetime import UTC, datetime\n",[207,796,797],{"class":209,"line":277},[207,798,799],{},"from pathlib import Path\n",[207,801,802],{"class":209,"line":284},[207,803,281],{"emptyLinePlaceholder":280},[207,805,806],{"class":209,"line":296},[207,807,808],{},"from embeint_htf_station.config import StageSettings, load_settings_from_yaml\n",[207,810,811],{"class":209,"line":591},[207,812,813],{},"from embeint_htf_station.stages.base import StageContext, StageLogger, StageResult\n",[207,815,816],{"class":209,"line":602},[207,817,818],{},"from embeint_htf_station.stations import BasicStation\n",[207,820,821],{"class":209,"line":607},[207,822,281],{"emptyLinePlaceholder":280},[207,824,825],{"class":209,"line":615},[207,826,281],{"emptyLinePlaceholder":280},[207,828,829],{"class":209,"line":626},[207,830,831],{},"class PrintDemoSecretStage:\n",[207,833,834],{"class":209,"line":637},[207,835,836],{},"    def __init__(self, settings: StageSettings) -> None:\n",[207,838,839],{"class":209,"line":642},[207,840,841],{},"        self.settings = settings\n",[207,843,844],{"class":209,"line":649},[207,845,281],{"emptyLinePlaceholder":280},[207,847,848],{"class":209,"line":660},[207,849,850],{},"    async def run(self, logger: StageLogger, context: StageContext) -> StageResult:\n",[207,852,853],{"class":209,"line":669},[207,854,855],{},"        started_at = datetime.now(UTC)\n",[207,857,858],{"class":209,"line":682},[207,859,860],{},"        value = context.require_secret(\"DEMO_MESSAGE\")\n",[207,862,863],{"class":209,"line":691},[207,864,865],{},"        await logger.log(\"info\", f\"Demo secret: {value}\")\n",[207,867,868],{"class":209,"line":696},[207,869,870],{},"        return StageResult(\n",[207,872,873],{"class":209,"line":707},[207,874,875],{},"            name=self.settings.name,\n",[207,877,879],{"class":209,"line":878},23,[207,880,881],{},"            outcome=\"passed\",\n",[207,883,885],{"class":209,"line":884},24,[207,886,887],{},"            started_at=started_at,\n",[207,889,891],{"class":209,"line":890},25,[207,892,893],{},"            finished_at=datetime.now(UTC),\n",[207,895,897],{"class":209,"line":896},26,[207,898,899],{},"        )\n",[207,901,903],{"class":209,"line":902},27,[207,904,281],{"emptyLinePlaceholder":280},[207,906,908],{"class":209,"line":907},28,[207,909,281],{"emptyLinePlaceholder":280},[207,911,913],{"class":209,"line":912},29,[207,914,915],{},"def main() -> None:\n",[207,917,919],{"class":209,"line":918},30,[207,920,921],{},"    parser = argparse.ArgumentParser()\n",[207,923,925],{"class":209,"line":924},31,[207,926,927],{},"    parser.add_argument(\"dut_id\", nargs=\"?\")\n",[207,929,931],{"class":209,"line":930},32,[207,932,933],{},"    parser.add_argument(\"--listen\", action=\"store_true\")\n",[207,935,937],{"class":209,"line":936},33,[207,938,939],{},"    parser.add_argument(\n",[207,941,943],{"class":209,"line":942},34,[207,944,945],{},"        \"--config\",\n",[207,947,949],{"class":209,"line":948},35,[207,950,951],{},"        type=Path,\n",[207,953,955],{"class":209,"line":954},36,[207,956,957],{},"        default=Path(__file__).with_name(\"config.yaml\"),\n",[207,959,961],{"class":209,"line":960},37,[207,962,963],{},"    )\n",[207,965,967],{"class":209,"line":966},38,[207,968,969],{},"    args = parser.parse_args()\n",[207,971,973],{"class":209,"line":972},39,[207,974,281],{"emptyLinePlaceholder":280},[207,976,978],{"class":209,"line":977},40,[207,979,980],{},"    settings = load_settings_from_yaml(args.config)\n",[207,982,984],{"class":209,"line":983},41,[207,985,986],{},"    station = BasicStation(\n",[207,988,990],{"class":209,"line":989},42,[207,991,992],{},"        settings,\n",[207,994,996],{"class":209,"line":995},43,[207,997,998],{},"        stage_factories={\"print_demo_secret\": PrintDemoSecretStage},\n",[207,1000,1002],{"class":209,"line":1001},44,[207,1003,963],{},[207,1005,1007],{"class":209,"line":1006},45,[207,1008,1009],{},"    if args.listen:\n",[207,1011,1013],{"class":209,"line":1012},46,[207,1014,1015],{},"        asyncio.run(station.serve_forever())\n",[207,1017,1019],{"class":209,"line":1018},47,[207,1020,1021],{},"    else:\n",[207,1023,1025],{"class":209,"line":1024},48,[207,1026,1027],{},"        if not args.dut_id:\n",[207,1029,1031],{"class":209,"line":1030},49,[207,1032,1033],{},"            parser.error(\"Enter a DUT ID, or use --listen\")\n",[207,1035,1037],{"class":209,"line":1036},50,[207,1038,1039],{},"        result = asyncio.run(station.run_once(args.dut_id))\n",[207,1041,1043],{"class":209,"line":1042},51,[207,1044,1045],{},"        print(f\"test finished: {result.outcome}\")\n",[207,1047,1049],{"class":209,"line":1048},52,[207,1050,281],{"emptyLinePlaceholder":280},[207,1052,1054],{"class":209,"line":1053},53,[207,1055,281],{"emptyLinePlaceholder":280},[207,1057,1059],{"class":209,"line":1058},54,[207,1060,1061],{},"if __name__ == \"__main__\":\n",[207,1063,1065],{"class":209,"line":1064},55,[207,1066,1067],{},"    main()\n",[1069,1070,1073],"callout",{"color":1071,"icon":1072},"warning","i-lucide-triangle-alert",[105,1074,1075,1076,1079],{},"This stage intentionally prints a disposable demo value. Stage logs are sent to HTF. Do not use this stage to print an API key, password, production token, or other real secret. In a production stage, call ",[109,1077,1078],{},"context.require_secret(\"NAME\")"," and use the result without logging it.",[142,1081,1083],{"id":1082},"_6-run-a-dut-and-check-the-output","6. Run a DUT and check the output",[105,1085,1086],{},"From the station repository root, run:",[198,1088,1090],{"className":359,"code":1089,"language":361,"meta":203,"style":203},"uv run python samples\u002Fbasic-station\u002Fmain.py DUT-001\n",[109,1091,1092],{"__ignoreMap":203},[207,1093,1094],{"class":209,"line":210},[207,1095,1089],{},[105,1097,1098],{},"The process connects with its certificate and MQTT credentials, pulls the runtime YAML and secret from HTF, runs both stages, and exits. The useful lines should look like this (timestamps are omitted here):",[198,1100,1105],{"className":1101,"code":1103,"language":1104,"meta":203},[1102],"language-text","=======Print DUT ID=======\n[Print DUT ID] - Testing DUT-001\n=======Print demo secret=======\n[Print demo secret] - Demo secret: Hello from HTF\ntest finished: passed\n","text",[109,1106,1103],{"__ignoreMap":203},[105,1108,1109,1110,1112,1113,1115],{},"A missing ",[109,1111,732],{}," causes the demo stage to fail. Check the secret name in HTF, the station API key in ",[109,1114,496],{},", and whether the process restarted after the secret was saved.",[105,1117,1118],{},"To accept runs from HTF, start the station in listening mode:",[198,1120,1122],{"className":359,"code":1121,"language":361,"meta":203,"style":203},"uv run python samples\u002Fbasic-station\u002Fmain.py --listen\n",[109,1123,1124],{"__ignoreMap":203},[207,1125,1126],{"class":209,"line":210},[207,1127,1121],{},[105,1129,1130,1131,1134,1135,1138,1139,1142,1143,1146,1147,1150,1151,1154,1155,1158],{},"Leave that process running. In HTF, open the station and wait for it to show ",[114,1132,1133],{},"Online",". Select ",[114,1136,1137],{},"Open kiosk view",", enter a DUT ID such as ",[109,1140,1141],{},"DUT-002",", and select ",[114,1144,1145],{},"Start test",". Watch the stage progress and ",[114,1148,1149],{},"Console output"," in the kiosk, then open the station's ",[114,1152,1153],{},"Run logs"," to inspect the saved result. Stop the listener with ",[109,1156,1157],{},"Ctrl+C"," when you are finished.",[142,1160,1162],{"id":1161},"if-it-does-not-connect","If it does not connect",[1164,1165,1166,1179],"table",{},[1167,1168,1169],"thead",{},[1170,1171,1172,1176],"tr",{},[1173,1174,1175],"th",{},"Symptom",[1173,1177,1178],{},"First check",[1180,1181,1182,1199,1207,1221,1233],"tbody",{},[1170,1183,1184,1188],{},[1185,1186,1187],"td",{},"Certificate or TLS error before connecting",[1185,1189,1190,1191,136,1193,1195,1196,1198],{},"Confirm ",[109,1192,340],{},[109,1194,343],{}," exist at the paths in ",[109,1197,496],{},", the key is readable by the station account, and the system clock is correct.",[1170,1200,1201,1204],{},[1185,1202,1203],{},"MQTT authentication fails",[1185,1205,1206],{},"Check the copied MQTT username and password and the broker host and port.",[1170,1208,1209,1212],{},[1185,1210,1211],{},"Secret is missing",[1185,1213,1214,1215,1217,1218,1220],{},"Check ",[109,1216,480],{},", the ",[109,1219,732],{}," name, and restart the station to fetch the latest secret.",[1170,1222,1223,1226],{},[1185,1224,1225],{},"Station stays offline",[1185,1227,1228,1229,1232],{},"Keep ",[109,1230,1231],{},"--listen"," running and check access to the MQTT broker.",[1170,1234,1235,1238],{},[1185,1236,1237],{},"Kiosk cannot start a test",[1185,1239,1240],{},"Wait for the station to be online and verify its saved runtime YAML has both stages.",[105,1242,1243,1244,1246,1247,1249,1250,1252],{},"See ",[126,1245,95],{"href":96}," for more checks and ",[126,1248,46],{"href":47}," for rotation and production use. When you are ready to add hardware flashing, follow ",[126,1251,63],{"href":64}," to upload and download a HEX archive and reference it from the station plan.",[1254,1255,1256],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":203,"searchDepth":210,"depth":221,"links":1258},[1259,1260,1261,1262,1263,1264,1265],{"id":144,"depth":221,"text":145},{"id":316,"depth":221,"text":317},{"id":404,"depth":221,"text":405},{"id":500,"depth":221,"text":501},{"id":722,"depth":221,"text":723},{"id":1082,"depth":221,"text":1083},{"id":1161,"depth":221,"text":1162},"Create a station, install its certificate and keys, add a demo secret, and run a DUT.","md",null,{},{"icon":15},{"title":12,"description":1266},"unlJ1lmXcUEQaYWTIada6nBfF_-cuCOby45Bloo3T-o",[1274,1276],{"title":5,"path":6,"stem":7,"description":1275,"icon":10,"children":-1},"Create a project, connect a station, and record your first hardware test.",{"title":17,"path":18,"stem":19,"description":1277,"icon":22,"children":-1},"Understand the main objects and data flow in Embeint HTF.",1790376318215]