UNPKG

@flowfuse/device-agent

Version:

An Edge Agent for running Node-RED instances deployed from the FlowFuse Platform

640 lines (574 loc) 35.3 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Device Agent - FlowFuse</title> <link rel="icon" href="assets/favicon.ico"> <link rel="icon" type="image/png" sizes="32x32" href="assets/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="16x16" href="assets/favicon-16x16.png"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <style> *, body, html { box-sizing: border-box; font-size: 14px; font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji' } body, html { height: 100%; text-align: center } .drop-shadow { filter: drop-shadow(0px 0px 6px rgba(0,0,0,.5)); } body { padding: 0; background: #f8f8f8; -moz-tab-size: 4; -o-tab-size: 4; tab-size: 4; border-width: 0; border-style: solid; border-color: rgba(229, 231, 235, 1); box-sizing: border-box; font-family: inherit; margin: 0; font-size: 14px; display: flex; min-height: 100vh; flex-direction: column; line-height: 1.5; letter-spacing: 0; color: rgba(107, 114, 128, 1); background-color: #1f2937; } .table { display: table; margin: auto; width: 100%; text-align: left; border-radius: .2rem; /* border-collapse: collapse; */ max-width: 680px; min-width: 420px; background: #f8f8f8; padding: 0px 0px 0px 0px; } .table-row { display: table-row; } .table-header { font-weight: bold; border: 2px solid #1f2937; color: #f8f8f8; background-color: rgb(188, 56, 56); } .table-cell { display: table-cell; border: 1px solid #1f2937; padding: 5px; } .table-cell.col-1 { width: 180px; } .ff-bg-light { background-color: #f9fafb } .text-gray-500 { color: rgba(107, 114, 128, 1) } header div a { color: #ed4e4e; font-size: 1.125rem; line-height: 1.75rem; text-decoration: none; } h1, h2, h3, h4 { position: relative } h1, h2, h3, h4, h5, h6, p { color: #8ce2e7; margin: 0 } h1 { font-size: 2.25rem; line-height: 3rem } #web-server-status { font-size: 3rem; color: #ed4e4e; } .section-header h1 { font-size: min(6vmin, 26px); min-width: 420px; color: #f8f8f8 } .hero-header { padding: 16px 20px 12px 20px; display: flex; align-items: center; background-color: #1f2937; min-width: 420px; } .hero-header-item { font-size: 24px; font-weight: 700 } .hero-header-item-1 { margin-right: 20px; width: 220px; padding-bottom: 7px } .hero-header-item-2 { text-align: center; padding-left: 18px; padding-bottom: 4px; font-weight: lighter; font-size: 28px } .hero-header-item-3 { text-align: right; flex: auto; } .uploader { display: block; clear: both; margin: 0 auto; width: 100%; max-width: 680px; min-width: 420px; } .hidden { display: none } .uploader textarea { /* white-space: nowrap; */ font-family: "DejaVu Sans Mono", monospace; float: left; clear: both; width: 100%; height: 11.5em; padding: .5rem .5rem; text-align: left; background: #fff; border-radius: .2rem; border: 3px solid #eee; transition: all .2s ease; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none } .uploader input[type=file] { display: none } .btn { display: inline-block; margin: 1.5rem .5rem 1rem .5rem; clear: both; font-family: inherit; font-weight: 700; font-size: 14px; text-decoration: none; text-transform: initial; border: none; border-radius: .2rem; outline: 0; padding: 0 1rem; height: 36px; line-height: 36px; color: #fff; text-transform: uppercase; transition: all .2s ease-in-out; box-sizing: border-box; background: #ed4e4e; border-color: #ed4e4e; cursor: pointer } .disabled .btn, .disabled #file-data { opacity: .5; cursor: not-allowed; } .px-6 { padding-left: 1.5rem; padding-right: 1.5rem } .mt-2 { margin-top: 0.5rem } .mt-4 { margin-top: 1rem } .w-full { width: 100% } .m-auto { margin: auto } .max-w-lg { max-width: 32rem } .max-w-2xl { max-width: 42rem } .container { position: relative; z-index: 2; width: 100% } .text-center { text-align: center } svg { display: block; vertical-align: middle } @media (max-width:600px) { .sm\:px-0 { padding-left: 0px; padding-right: 0px } .sm\:px-2 { padding-left: 2px; padding-right: 2px } .sm\:pb-2 { padding-bottom: 2px } .sm\:mt-4{ margin-top: 4px } .hero-header { /* flex-direction: column; */ padding: 5px 5px 5px 5px !important; } .hero-header-item-1 { margin-right: 0; margin-bottom: 0px; padding-bottom: 0px; } .hero-header-item-2 { display: none; } } </style> </head> <body> <div class="text-gray-500 min-h-screen flex flex-col"> <header class="hero-header sm:py-2 drop-shadow"> <div class="hero-header-item hero-header-item-1"> <a id="link-to-forge" class="ff-logo ff-wordmark no-underline hover:no-underline" href="/"> <!--?xml version="1.0" encoding="UTF-8"?--> <svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" id="Layer_1" x="0" y="0" style="enable-background:new 0 0 402 89.7" version="1.1" viewBox="0 0 402 89.7"><style>.st0{fill:#ed4e4e}.st1{fill:#fff}</style><path d="M45.8 50.2c8.5 3.6 16.6 7.9 25.5 9.8 3.9.7 7.9 1 11.8 1V39.4h-3.3c-12.1 0-23.2 6.2-34 10.8zM20.7 57.1c-3.9-.3-7.8-.3-11.7-.3H0v19.9c0 3.6 2.9 6.5 6.5 6.5h70.1c3.6 0 6.5-2.9 6.5-6.5v-2.5c-8.5 0-17.3-1.1-25.2-4.4-12.4-4.3-23.8-12.4-37.2-12.7zM76.6 0H6.5C2.9 0 0 2.9 0 6.5v36.8c7.8 0 16.1.3 24-.3 10.8-1 20.2-6.9 30.4-10.8 9.1-3.9 19-6.2 28.8-5.9V6.5c-.1-3.6-3-6.5-6.6-6.5z" class="st0"/><path d="M83.1 39.4V26.3c-9.8-.3-19.6 2-28.8 5.9C44.2 36.1 34.7 42 24 43c-7.8.7-16.1.3-24 .3v13.4h8.9c3.9 0 7.8 0 11.7.3 13.4.3 24.8 8.4 37.2 12.7 7.8 3.3 16.7 4.4 25.2 4.4V61c-3.9 0-7.9-.3-11.8-1-8.8-2-17-6.2-25.5-9.8 10.8-4.6 21.9-10.8 34-10.8h3.4zM134.2.2h-22.3c-1.5 0-2.8.5-3.7 1.4-.9.9-1.4 2.1-1.4 3.7v37.8c0 1.6.4 2.7 1.2 3.4.8.7 2.1 1 3.9 1 .9 0 1.7-.1 2.5-.2s1.4-.2 1.8-.3V28.4H129c1.1 0 1.9-.3 2.5-1 .6-.7.9-1.7.9-2.9 0-.8-.1-1.5-.3-2.1-.2-.6-.5-1.2-.7-1.6h-15.2V7.9h15.4c1.1 0 1.9-.3 2.5-1 .6-.7.9-1.7.9-3 0-.8-.1-1.5-.3-2.1 0-.8-.2-1.3-.5-1.6zM145.6.2c-.9 0-1.7.1-2.4.2-.8.2-1.4.3-1.8.4v42.4c0 1.6.4 2.7 1.3 3.3.8.6 2.1.9 3.8.9.9 0 1.7-.1 2.5-.2.7-.1 1.3-.3 1.7-.4V4.5c0-1.6-.4-2.7-1.2-3.3-.9-.7-2.2-1-3.9-1zM187.7 12.1c-2.8-1.6-6.1-2.4-9.9-2.4-3.7 0-7 .8-9.9 2.4-2.9 1.6-5.1 3.8-6.6 6.7-1.6 2.9-2.4 6.3-2.4 10.2 0 3.9.8 7.3 2.3 10.2 1.6 2.9 3.8 5.1 6.6 6.6 2.9 1.6 6.2 2.3 10 2.3s7.2-.8 10-2.3c2.8-1.6 5-3.8 6.6-6.7 1.6-2.9 2.3-6.3 2.3-10.1 0-3.9-.8-7.3-2.4-10.2-1.5-2.9-3.7-5.1-6.6-6.7zm-3 25.5c-1.7 2.1-3.9 3.1-6.8 3.1s-5.2-1-6.8-3.1c-1.7-2-2.5-5-2.5-8.7 0-3.7.8-6.6 2.5-8.7 1.7-2.1 4-3.1 6.8-3.1 2.9 0 5.2 1 6.8 3.1 1.7 2.1 2.5 5 2.5 8.7 0 3.8-.9 6.7-2.5 8.7zM288.4.2h-22.3c-1.5 0-2.8.5-3.7 1.4S261 3.7 261 5.3v37.8c0 1.6.4 2.7 1.2 3.4.8.7 2.1 1 3.9 1 .9 0 1.7-.1 2.5-.2s1.4-.2 1.8-.3V28.4h12.8c1.1 0 1.9-.3 2.5-1 .6-.7.9-1.7.9-2.9 0-.8-.1-1.5-.3-2.1-.2-.6-.5-1.2-.7-1.6h-15.2V7.9H286c1.1 0 1.9-.3 2.5-1 .6-.7.9-1.7.9-3 0-.8-.1-1.5-.3-2.1-.2-.8-.4-1.3-.7-1.6zM252.5 11c-.8-.6-1.9-.8-3.1-.8-1.5 0-2.9.4-4.1 1.1-.7 4.7-1.5 9.4-2.5 14.2-1 4.8-2 9.3-2.9 13.5h-.4c-.3-1.2-.7-3-1.4-5.4-.7-2.4-1.5-5.1-2.4-8.2-.9-3.1-1.9-6.3-2.9-9.5-.3-.9-.9-1.6-1.8-2-.9-.4-2-.7-3.3-.7-1 0-1.9.1-2.7.3-.8.2-1.5.5-2.1.9-1 3.4-2 6.8-3 10.1s-1.9 6.3-2.6 8.8c-.7 2.6-1.3 4.5-1.6 5.6h-.3c-.2-.8-.5-2-.8-3.6-.4-1.6-.8-3.4-1.2-5.5-.5-2.1-.9-4.5-1.4-7.2-.5-2.7-1-5.4-1.4-8.3-.3-1.5-.8-2.6-1.5-3.3-.8-.6-1.8-1-3.1-1-1.1 0-2 .2-2.9.6-.8.4-1.5.9-2 1.4.4 1.9.8 4.2 1.3 6.7s1.1 5.1 1.8 7.7c.6 2.7 1.3 5.2 1.9 7.7.6 2.5 1.3 4.7 1.9 6.5.6 1.9 1.2 3.3 1.6 4.4.4.8 1.1 1.4 2.2 1.9 1.1.4 2.3.7 3.7.7 1.2 0 2.3-.1 3.1-.4.8-.3 1.5-.6 2-1 .6-1.7 1.3-3.8 2.2-6.3.8-2.5 1.7-5.3 2.6-8.2.9-2.9 1.7-5.8 2.4-8.6l2.4 8.4c.8 2.8 1.6 5.5 2.4 7.9.8 2.4 1.5 4.4 2.1 5.9.5 1.5 2.3 2.3 5.4 2.3 1.2 0 2.3-.1 3.3-.4 1-.3 1.7-.6 2.2-1 .4-1 .9-2.4 1.5-4.1.6-1.7 1.2-3.7 1.8-5.9.6-2.2 1.3-4.5 1.9-6.8.6-2.3 1.2-4.6 1.6-6.7.5-2.1.9-3.9 1.2-5.6.3-1.6.5-2.7.5-3.4-.4-1.2-.8-2.2-1.6-2.7zM321.5 10.3c-.9 0-1.7.1-2.4.2-.8.2-1.4.3-1.8.4V39.1c-.6.4-1.5.7-2.6 1-1.1.3-2.5.5-4.1.5-2.6 0-4.5-.6-6-1.8-1.4-1.2-2.1-3.2-2.1-5.9V14.6c0-1.7-.4-2.8-1.2-3.4-.8-.6-2.1-.9-3.8-.9-.9 0-1.8.1-2.5.2-.8.2-1.3.3-1.7.4v22.2c0 3.5.7 6.3 2.1 8.5 1.4 2.2 3.4 3.8 6 4.8s5.7 1.5 9.2 1.5c2.9 0 5.5-.3 7.7-.9 2.2-.6 4.1-1.3 5.5-2.2 1.1-.7 1.8-1.4 2.2-2.1.4-.8.6-1.7.6-2.9V14.6c0-1.7-.4-2.8-1.2-3.4-.9-.6-2.2-.9-3.9-.9zM350.6 25.4l-4-1c-1.6-.4-2.7-.9-3.4-1.5-.7-.6-1-1.4-1-2.3 0-1.2.6-2.2 1.8-2.8 1.2-.6 2.8-1 4.7-1 1.3 0 2.6.1 3.9.4 1.3.3 2.5.6 3.6 1.1 1.1.4 1.9.9 2.5 1.3.6-.4 1-.9 1.4-1.6.4-.7.6-1.4.6-2.3 0-1.3-.5-2.4-1.6-3.3-1.1-.9-2.6-1.6-4.5-2.1s-4.1-.7-6.5-.7c-4.7 0-8.3 1-10.9 3.1-2.6 2.1-3.9 4.7-3.9 7.8 0 2.9.9 5.2 2.6 6.9 1.8 1.7 4.5 3 8.2 3.8l4.5 1.1c1.6.4 2.8.9 3.6 1.6.8.7 1.1 1.6 1.1 2.8 0 2.7-2.2 4.1-6.6 4.1-2.4 0-4.5-.4-6.4-1.1-1.9-.7-3.5-1.6-5-2.5-.7.5-1.2 1-1.6 1.8-.4.7-.6 1.5-.6 2.4 0 2 1.3 3.6 3.9 4.8 2.6 1.2 5.9 1.8 10 1.8 5 0 8.8-1 11.4-3 2.6-2 3.9-4.8 3.9-8.3 0-3-.9-5.4-2.8-7.2-1.8-1.8-4.8-3.2-8.9-4.1zM400.9 28.8c.7-.7 1.1-1.7 1.1-3.2 0-3.1-.7-5.8-2.1-8.2-1.4-2.4-3.4-4.3-5.9-5.7-2.5-1.4-5.4-2.1-8.8-2.1-2.4 0-4.8.4-6.9 1.2-2.2.8-4.1 2-5.7 3.6-1.7 1.6-3 3.5-3.9 5.9-.9 2.3-1.4 5.1-1.4 8.2 0 4.3.8 7.9 2.5 10.8 1.7 2.9 4 5.1 7.1 6.5 3 1.5 6.5 2.2 10.5 2.2 2.6 0 4.8-.3 6.8-.8s3.5-1.2 4.7-2.2c1.2-.9 1.8-2.1 1.8-3.3 0-.8-.2-1.6-.7-2.3-.4-.7-1-1.2-1.6-1.6-1.1.8-2.6 1.5-4.4 2.1-1.8.6-3.8 1-5.9 1-3.2 0-5.9-.8-8-2.4-1.7-1.3-2.8-3.1-3.4-5.4l21.5-3.1c1-.1 2-.5 2.7-1.2zm-25-2.1c.2-3.1 1-5.5 2.7-7.2 1.8-1.8 4-2.8 6.6-2.8 2.6 0 4.5.7 5.9 2.2 1.4 1.5 2.1 3.2 2.3 5.2l-17.5 2.6zM115.2 65.3c.7 0 1.4.1 2 .2.6.2 1.2.3 1.7.6v-6.8c.2 0 .4-.1.7-.2.3-.1.6-.1 1-.1.7 0 1.2.1 1.6.4.3.3.5.7.5 1.3v18.8c0 .5-.1.9-.3 1.2-.2.3-.5.6-1 .9-.5.3-1.3.7-2.3 1-1 .3-2 .4-3.2.4-1.4 0-2.6-.2-3.7-.5s-2.1-.9-2.9-1.6-1.4-1.7-1.8-2.8c-.4-1.1-.6-2.4-.6-3.9s.2-2.9.7-4c.4-1.1 1-2.1 1.8-2.8.8-.7 1.7-1.3 2.7-1.6.9-.3 2-.5 3.1-.5zm3.7 4c-.4-.3-.9-.5-1.4-.7-.5-.2-1.1-.3-1.8-.3s-1.4.1-2 .3c-.6.2-1.1.6-1.6 1-.4.5-.8 1.1-1.1 1.9s-.4 1.7-.4 2.7c0 2 .5 3.5 1.4 4.4 1 .9 2.2 1.4 3.8 1.4.7 0 1.3-.1 1.8-.2s.9-.3 1.3-.5v-10zM130.6 76.1c.3 1.4 1 2.4 1.9 3 1 .6 2.2 1 3.7 1 1 0 1.9-.2 2.8-.5.8-.3 1.5-.6 2-1 .7.4 1 .9 1 1.6 0 .4-.2.8-.5 1.1-.3.3-.7.6-1.3.9s-1.2.4-1.9.6c-.7.2-1.5.2-2.3.2-1.4 0-2.6-.2-3.8-.6s-2.1-1-2.9-1.7c-.8-.8-1.4-1.7-1.9-2.8-.4-1.1-.7-2.4-.7-3.9s.2-2.7.6-3.8c.4-1.1 1-2 1.7-2.7.7-.7 1.6-1.3 2.6-1.7 1-.4 2.1-.6 3.2-.6 1.2 0 2.2.2 3.1.5 1 .4 1.7.9 2.4 1.5.7.7 1.2 1.4 1.6 2.3s.6 1.9.6 3c0 .6-.2 1-.4 1.3-.3.3-.7.4-1.2.5l-10.3 1.8zm4.4-7.9c-1.3 0-2.4.4-3.3 1.3-.9.9-1.3 2.2-1.4 3.8l8.8-1.2c-.1-1.1-.5-2-1.2-2.7-.5-.8-1.5-1.2-2.9-1.2zM159.2 65.5c.5 0 1 .1 1.3.4.3.3.5.7.5 1.2 0 .3-.1.8-.3 1.5-.2.7-.4 1.5-.7 2.4s-.7 1.9-1 3c-.4 1.1-.8 2.1-1.2 3.1-.4 1-.9 2-1.3 3s-.8 1.7-1.2 2.4c-.2.1-.5.2-.9.3-.4.1-.8.2-1.3.2-1.2 0-1.9-.3-2.3-1-.3-.6-.8-1.5-1.3-2.8-.5-1.3-1.1-2.7-1.7-4.2-.6-1.5-1.1-3-1.7-4.5-.5-1.5-1-2.8-1.3-3.9.2-.3.5-.5.9-.7.3-.2.7-.3 1.1-.3.5 0 1 .1 1.3.4s.6.7.8 1.3l2 6.2c.1.4.3 1 .5 1.5s.4 1.1.6 1.7c.2.6.4 1.1.6 1.6.2.5.3 1 .5 1.3h.2c.8-2.3 1.6-4.5 2.3-6.8s1.4-4.5 1.9-6.8c.5-.4 1-.5 1.7-.5zM164.4 60.7c0-.6.2-1.1.6-1.6.4-.4 1-.6 1.7-.6s1.2.2 1.6.6c.4.4.6 1 .6 1.6 0 .6-.2 1.1-.6 1.6-.4.4-1 .6-1.6.6-.7 0-1.2-.2-1.7-.6-.4-.5-.6-1-.6-1.6zm4.1 21.8c-.2.1-.4.1-.7.2-.3.1-.6.1-1 .1-.7 0-1.2-.1-1.6-.4s-.5-.7-.5-1.3V65.9c.2 0 .4-.1.7-.2.3-.1.6-.1 1-.1.7 0 1.2.1 1.5.4s.5.7.5 1.4l.1 15.1zM181.9 68.3c-.8 0-1.5.1-2.1.4-.7.3-1.2.6-1.7 1.1-.4.5-.8 1.1-1.1 1.8s-.4 1.6-.4 2.5c0 1.9.5 3.3 1.5 4.3s2.3 1.5 3.8 1.5c.9 0 1.7-.1 2.3-.3.6-.2 1.2-.5 1.7-.9.3.2.6.4.8.7.2.3.3.6.3 1 0 .7-.5 1.4-1.5 1.9s-2.3.8-3.8.8c-1.3 0-2.5-.2-3.6-.5s-2-.9-2.8-1.7c-.8-.7-1.4-1.7-1.8-2.8-.4-1.1-.7-2.4-.7-3.9s.2-2.8.7-3.9c.5-1.1 1.1-2.1 1.9-2.8s1.7-1.3 2.8-1.7c1.1-.4 2.2-.5 3.4-.5.8 0 1.5.1 2.1.2s1.2.3 1.6.6c.4.3.8.5 1 .9.3.3.4.7.4 1s-.1.6-.3.9c-.2.3-.4.5-.7.6-.5-.3-1-.6-1.7-.8-.4-.2-1.2-.3-2.1-.4zM193.3 76.1c.3 1.4 1 2.4 1.9 3 1 .6 2.2 1 3.7 1 1 0 1.9-.2 2.8-.5.8-.3 1.5-.6 2-1 .7.4 1 .9 1 1.6 0 .4-.2.8-.5 1.1-.3.3-.7.6-1.3.9-.5.3-1.2.4-1.9.6-.7.2-1.5.2-2.3.2-1.4 0-2.6-.2-3.8-.6s-2.1-1-2.9-1.7c-.8-.8-1.4-1.7-1.9-2.8s-.7-2.4-.7-3.9.2-2.7.6-3.8c.4-1.1 1-2 1.7-2.7.7-.7 1.6-1.3 2.6-1.7 1-.4 2.1-.6 3.2-.6s2.2.2 3.1.5c1 .4 1.7.9 2.4 1.5.7.7 1.2 1.4 1.6 2.3.4.9.6 1.9.6 3 0 .6-.2 1-.4 1.3-.3.3-.7.4-1.2.5l-10.3 1.8zm4.5-7.9c-1.3 0-2.4.4-3.3 1.3-.9.9-1.3 2.2-1.4 3.8l8.8-1.2c-.1-1.1-.5-2-1.2-2.7-.6-.8-1.6-1.2-2.9-1.2zM222.7 65.3c2.2 0 4 .5 5.3 1.5 1.3 1 2 2.5 2 4.6v8.3c0 .5-.1 1-.4 1.3-.3.3-.6.6-1 .9-.6.3-1.4.7-2.4.9-1 .3-2.1.4-3.4.4-2.3 0-4.1-.4-5.4-1.4-1.3-.9-1.9-2.3-1.9-4 0-1.6.5-2.8 1.5-3.6 1-.8 2.5-1.3 4.4-1.6l5-.5v-.8c0-1-.3-1.8-1-2.3-.7-.5-1.6-.7-2.9-.7-1 0-1.9.1-2.8.4-.9.3-1.7.6-2.3.9-.3-.2-.4-.4-.6-.7-.2-.3-.3-.6-.3-.9 0-.4.1-.7.3-1 .2-.3.5-.5 1-.7.7-.3 1.4-.6 2.3-.7.7-.3 1.7-.3 2.6-.3zm0 14.8c.8 0 1.6-.1 2.2-.3.6-.2 1-.3 1.3-.5v-4.5l-3.9.4c-1.1.1-1.9.3-2.5.7-.5.4-.8.9-.8 1.7 0 .8.3 1.4.9 1.8.7.5 1.6.7 2.8.7zM245.7 81c-.4.3-1 .5-1.7.7-.7.2-1.4.3-2.3.3-1 0-2-.2-3-.4-1-.3-1.8-.8-2.5-1.4-.7-.7-1.3-1.5-1.7-2.6-.4-1-.6-2.3-.6-3.9 0-1.4.2-2.6.6-3.6.4-1 1-1.9 1.7-2.6.8-.7 1.7-1.2 2.7-1.6 1-.3 2.2-.5 3.5-.5 1.2 0 2.3.2 3.2.4 1 .3 1.8.7 2.4 1 .4.3.7.6 1 .9.2.3.3.7.3 1.2V83c0 1.2-.2 2.2-.6 3.1-.4.9-1 1.6-1.7 2.1s-1.6.9-2.6 1.2-2.1.4-3.2.4c-1.2 0-2.3-.1-3.1-.3-.9-.3-1.5-.5-1.9-.7-.7-.4-1.1-1-1.1-1.8 0-.4.1-.8.3-1 .2-.3.4-.5.6-.7.5.4 1.3.7 2.2 1 .9.3 1.9.5 2.9.5 1.5 0 2.7-.3 3.5-.9.8-.6 1.2-1.6 1.2-3l-.1-1.9zm-3.5-2c.8 0 1.5-.1 2-.3.5-.2 1-.5 1.4-.9v-8.7c-.3-.2-.8-.4-1.3-.6-.5-.2-1.1-.3-1.9-.3-1.5 0-2.6.4-3.5 1.3-.9.9-1.3 2.2-1.3 4 0 1 .1 1.8.3 2.5.3.7.6 1.2 1 1.7s.9.7 1.5.9c.5.3 1.1.4 1.8.4zM257.1 76.1c.3 1.4 1 2.4 1.9 3 1 .6 2.2 1 3.7 1 1 0 1.9-.2 2.8-.5.8-.3 1.5-.6 2-1 .7.4 1 .9 1 1.6 0 .4-.2.8-.5 1.1-.3.3-.7.6-1.3.9-.5.3-1.2.4-1.9.6-.7.2-1.5.2-2.3.2-1.4 0-2.6-.2-3.8-.6-1.1-.4-2.1-1-2.9-1.7-.8-.8-1.4-1.7-1.9-2.8-.4-1.1-.7-2.4-.7-3.9s.2-2.7.6-3.8c.4-1.1 1-2 1.7-2.7.7-.7 1.6-1.3 2.6-1.7 1-.4 2.1-.6 3.2-.6 1.2 0 2.2.2 3.1.5 1 .4 1.7.9 2.4 1.5.7.7 1.2 1.4 1.6 2.3s.5 1.9.5 3c0 .6-.1 1-.4 1.3-.3.3-.7.4-1.2.5l-10.2 1.8zm4.5-7.9c-1.3 0-2.4.4-3.3 1.3-.9.9-1.3 2.2-1.4 3.8l8.8-1.2c-.1-1.1-.5-2-1.2-2.7-.6-.8-1.6-1.2-2.9-1.2zM283.4 69.1c-.7-.5-1.6-.8-2.7-.8-.8 0-1.6.1-2.3.3s-1.2.4-1.7.7v13.1c-.2.1-.4.1-.7.2s-.6.1-1 .1c-.7 0-1.2-.1-1.5-.4-.3-.3-.5-.7-.5-1.3V69.3c0-.5.1-1 .3-1.3.2-.3.6-.7 1.1-1 .7-.4 1.6-.8 2.6-1.1 1.1-.3 2.3-.5 3.6-.5 2.3 0 4.1.5 5.4 1.6 1.3 1 2 2.6 2 4.7v10.9c-.2.1-.4.1-.7.2s-.6.1-1 .1c-.7 0-1.2-.1-1.6-.4-.3-.3-.5-.7-.5-1.3v-9.3c.1-1.4-.2-2.2-.8-2.8zM297.6 79.5c.5.3 1.2.5 2.1.5.4 0 .8-.1 1.3-.2.4-.1.9-.3 1.2-.4.2.2.3.4.4.6.1.3.2.5.2.9 0 .6-.3 1.1-1 1.5s-1.5.6-2.8.6c-1.8 0-3.2-.4-4.3-1.2-1-.8-1.6-2.1-1.6-4V61.1c.2 0 .4-.1.7-.2.3-.1.6-.1 1-.1.7 0 1.2.1 1.5.4.3.3.5.7.5 1.3v3.6h5.6s.2.3.3.6c.1.3.1.5.1.8 0 1-.4 1.6-1.3 1.6h-4.6v8.5c0 .8.2 1.4.7 1.9zM22.6 13.1h-9.5v9.5h9.5v-9.5z" class="st1"/><path d="M29.7 20.8h-3v-5.9h3c.3 0 .6-.3.6-.6s-.3-.6-.6-.6h-3v-1c0-2.1-1.7-3.8-3.7-3.8h-1V6c0-.3-.3-.6-.6-.6s-.6.2-.6.6v3h-5.9V6c0-.3-.3-.6-.6-.6s-.6.2-.6.6v3h-1.1c-2 0-3.7 1.7-3.7 3.8v1H6c-.3 0-.6.3-.6.6s.3.6.6.6h3v5.9H6c-.3 0-.6.3-.6.6s.2.5.6.5h3v1c0 2.1 1.7 3.8 3.7 3.8h1.1v3c0 .3.3.6.6.6s.6-.3.6-.6v-3h5.9v3c0 .3.3.6.6.6s.5-.4.5-.7v-3h1c2 0 3.7-1.7 3.7-3.8v-1h3c.3 0 .6-.3.6-.6s-.3-.5-.6-.5zM25.5 23c0 1.4-1.1 2.6-2.5 2.6H12.6c-1.4 0-2.5-1.2-2.5-2.6V12.7c0-1.4 1.1-2.6 2.5-2.6H23c1.4 0 2.5 1.2 2.5 2.6V23z" class="st1"/></svg> </a> </div> <div class="hero-header-item hero-header-item-3"><a class="flex items-center gap-2" href="https://flowfuse.com/docs/device-agent/introduction/" target="_blank">docs</a></div> </header> <main> <!-- Status title --> <div class="section-header w-full px-6 sm:px-0 pb-2 mt-2 sm:py-2 sm:my-2"> <div class="container m-auto text-center max-w-2xl"> <h1 class="max-w-lg m-auto"> Agent Information </h1> </div> </div> <!-- Status data --> <div class="w-full px-6 pb-2 sm:px-2"> <div class="container m-auto text-center max-w-4xl"> <div class="m-auto w-full text-center"> <div id="status-table" class="table"></div> <div id="config-table" class="table mt-4"></div> </div> </div> </div> <!-- Config Upload title --> <div class="section-header w-full px-6 sm:px-0 pb-2 mt-2 sm:py-2 sm:my-2"> <div class="container m-auto text-center max-w-2xl"> <h1 class="max-w-lg m-auto"> Agent Configuration </h1> </div> </div> <!-- Config Upload text input and buttons --> <div class="w-full px-6 sm:px-2"> <div class="container m-auto text-center max-w-4xl"> <div class="m-auto w-full text-center"> <form id="file-upload-form" class="uploader" enctype="multipart/form-data"> <input id="file-upload" type="file" name="fileUpload" accept=".yml,.txt" /> <label for="file-upload" id="file-drop-target"> <textarea id="file-data" spellcheck="false" name="deviceText" rows="7" cols="80" ></textarea> <span id="file-upload-btn" class="btn" style="width: 125px;">Load file</span> </label> <button type="button" class="btn" id="submit-button" style="width: 125px;"> Apply</button> </form> </div> </div> </div> <h2 id="web-server-status" class="text-center hidden">Web server offline</h2> </main> </div> <script> function main () { const fileSelect = document.getElementById('file-upload') const fileData = document.getElementById('file-data') const fileDropTarget = document.getElementById('file-drop-target') const submitButton = document.getElementById('submit-button') const uploadButton = document.getElementById('file-upload-btn') function InitDropLoader () { // hand the file upload input to the file drop target handler fileSelect.addEventListener('change', fileSelectHandler, false) // Is XHR2 available? const xhr = new XMLHttpRequest() if (xhr.upload) { // File Drop fileDropTarget.addEventListener('drop', fileSelectHandler, false) fileData.addEventListener('drop', fileSelectHandler, false) document.addEventListener('dragover', (e) => { const validDropPoints = ['file-drop-target', 'file-data'] const items = e.dataTransfer.items if (items.length > 1 || items.length === 0 || !validDropPoints.includes(e.target.id)) { e.dataTransfer.dropEffect = 'none' e.preventDefault() return true } e.dataTransfer.dropEffect = 'copy' return false }, false) document.addEventListener('drop', (e) => { e.preventDefault() return true }, false) } } /** @param {DragEvent} e */ function fileSelectHandler (e) { /** @type {FileList} */ const files = e.target.files || e.dataTransfer.files // only accept one file if (files.length !== 1) { alert('Please select a single file.') // cancel the drop e.stopPropagation() e.preventDefault() return false } // the file to process const file = files[0] // get the file type let fType = file.type // if the file type is empty, try to determine it from the file name if (fType === '') { const ext = file.name.split('.').pop() if (ext === 'yml' || ext === 'yaml') { fType = 'text/yaml' } else if (ext === 'txt') { fType = 'text/plain' } } // ensure file is of correct type if (fType !== 'text/plain' && fType !== 'text/yaml') { alert('Invalid file type. Please select a .txt or .yml file.') e.stopPropagation() e.preventDefault() return false } e.stopPropagation() e.preventDefault() // clear the text area and load the file fileData.innerText = '' const reader = new FileReader() reader.onload = function (event) { fileData.value = event.target.result.replace(/\\n/g, '\n').replace(/\t/g, ' ') fileSelect.value = '' // clear the file selector updateTextareaWrapping() } reader.readAsText(file) updateTextareaWrapping() return true } /** submit button handler */ function submitHandler () { const xhrSubmitConfig = new XMLHttpRequest() xhrSubmitConfig.timeout = 5000 // Set timeout to 5 seconds (5000 milliseconds) xhrSubmitConfig.ontimeout = function () { alert('Request timed out!') } xhrSubmitConfig.onreadystatechange = function () { if (xhrSubmitConfig.readyState === XMLHttpRequest.DONE) { if (xhrSubmitConfig.status === 200) { fileData.value = '' alert('Configuration saved. The device agent will now reload with this new configuration.') } else { const data = typeof xhrSubmitConfig.response === 'object' ? xhrSubmitConfig.response : JSON.parse(xhrSubmitConfig.responseText || '{}') const message = data.error || 'There was a problem with the request.' alert(message) } } } xhrSubmitConfig.open('POST', 'submit') xhrSubmitConfig.setRequestHeader('Content-type', 'application/json') xhrSubmitConfig.setRequestHeader('Accept', 'application/json') xhrSubmitConfig.timeout = 5000 // Set timeout to 5 seconds (5000 milliseconds) xhrSubmitConfig.responseType = 'json' const data = { config: fileData.value } xhrSubmitConfig.send(encodeURIComponent(JSON.stringify(data))) statusPoll() return false // prevent page refresh } /** update the link to the forge */ function updateLinkToForge (forgeURL, target) { const linkToForge = document.getElementById('link-to-forge') if (linkToForge) { linkToForge.href = forgeURL linkToForge.target = target } } /** add/remove wrapping to the text area to show/hide line breaks in the placeholder text */ function updateTextareaWrapping () { fileData.style.whiteSpace = fileData.value.length > 0 ? 'nowrap' : '' } submitButton.addEventListener('click', submitHandler, false) if (window.File && window.FileList && window.FileReader) { fileData.placeholder = 'Enter the device or a provisioning configuration yaml. \nYou can load a file, drop a file or paste text in directly. \nClick Apply to update and reload the device.' InitDropLoader() } else { fileData.placeholder = 'Enter the device configuration.' uploadButton.style.display = 'none' } // listen for changes to the text area and wrap/nowrap the text as // needed to show line breaks in the placeholder text 'change input cut paste focus blur'.split(' ').forEach(evName => { fileData.addEventListener(evName, updateTextareaWrapping, false) }) // status updates const statusPollInterval = 5000 let statusPollTimeoutCount = 0 let statusPollTimer = null function statusPoll () { const showOfflineMessage = () => { const webServerStatus = document.getElementById('web-server-status') if (webServerStatus) { webServerStatus.classList.remove('hidden') } } const xhrStatus = new XMLHttpRequest() xhrStatus.ontimeout = function () { if (statusPollTimeoutCount < 3) { statusPollTimeoutCount++ return } clearInterval(statusPollTimer) showOfflineMessage() } xhrStatus.onerror = function () { if (statusPollTimeoutCount < 3) { statusPollTimeoutCount++ return } clearInterval(statusPollTimer) showOfflineMessage() } xhrStatus.onreadystatechange = function () { if (xhrStatus.readyState === XMLHttpRequest.DONE) { if (xhrStatus.status === 200) { const data = xhrStatus.response || {} const status = data.status || {} const deviceClock = status.deviceClock ? new Date(status.deviceClock) : 'unknown' const config = status.config || {} const statusTable = document.getElementById('status-table') const configTable = document.getElementById('config-table') const tempTable = document.createElement('div') // for building the tables before adding to the DOM const addRow = (table, key, value, classes, skipIfNull, defaultForNull = 'unknown') => { const { headerClass, rowClass, cellClass } = Object.assign({}, classes, { rowClass: 'table-row', cellClass: 'table-cell' }) if (skipIfNull && (value === null || value === undefined)) { return } if (value === null || value === undefined) { value = defaultForNull } const row = document.createElement('div') row.classList.add(rowClass) if (headerClass) { row.classList.add(headerClass) } const keyCell = document.createElement('div') keyCell.classList.add(cellClass, 'col-1') keyCell.textContent = key const valueCell = document.createElement('div') valueCell.classList.add(cellClass, 'col-2') valueCell.textContent = value row.appendChild(keyCell) row.appendChild(valueCell) table.appendChild(row) } // ** status table ** // tempTable.innerHTML = '' // Create the header row addRow(tempTable, 'Status', 'Value', { headerClass: 'table-header' }) // add the status & version rows addRow(tempTable, 'Agent Status', status.state) addRow(tempTable, 'Developer Mode', status.mode === 'developer' ? 'true' : undefined, null, true) addRow(tempTable, 'Snapshot Name', status.snapshotName, null, false, 'none') addRow(tempTable, 'Snapshot Desc', status.snapshotDesc, null, true) addRow(tempTable, 'Agent Version', status.version) addRow(tempTable, 'Device Clock', deviceClock) statusTable.innerHTML = tempTable.innerHTML // ** config table ** // tempTable.innerHTML = '' // add the config header row addRow(tempTable, 'Configuration', 'Value', { headerClass: 'table-header' }) // add the config rows addRow(tempTable, 'Device ID', config.deviceId || 'not set') addRow(tempTable, 'Device Name', status.name || 'not set') addRow(tempTable, 'Forge URL', config.forgeURL || 'not set') addRow(tempTable, 'Working Directory', config.dir || 'not set') addRow(tempTable, 'Device File', config.deviceFile || 'unknown') addRow(tempTable, 'Port', config.port || 'unknown') if (config.provisioningMode) { addRow(tempTable, 'Provisioning Mode', config.provisioningMode) addRow(tempTable, 'Provisioning Name', config.provisioningName) addRow(tempTable, 'Provisioning Team', config.provisioningTeam) } configTable.innerHTML = tempTable.innerHTML tempTable.innerHTML = '' // update the hyperlink to the forge if (config.forgeURL) { updateLinkToForge(config.forgeURL, '_forge') } else { updateLinkToForge('/', '_self') } } } } xhrStatus.open('GET', 'status') xhrStatus.setRequestHeader('Content-type', 'application/json') xhrStatus.setRequestHeader('Accept', 'application/json') xhrStatus.responseType = 'json' xhrStatus.timeout = statusPollInterval - 200 // Set timeout to statusPollInterval less 200ms for grace xhrStatus.send() } // start the status poll statusPollTimer = setInterval(() => { statusPoll() }, statusPollInterval) // initial poll for status statusPoll() } main() </script> </body> </html>