implemented connected devices page

This commit is contained in:
robert committed 2023-06-13 03:33:11 -04:00
1 parent 7ea2093a91
commit 6f69ad93c1
3 files changed
+125 -1

No files matched your search

+69
View File
@@ -20,6 +20,46 @@ struct event {
const char *text;
};
// Connected Devices
struct device {
char *dev_name;
char *mac;
char *ip_addr;
int speed;
char *connected_to;
int lease_time_left;
char *last_seen;
};
static struct device s_devices[] = {
{ .dev_name = "espressif",
.mac = "02:11:22:33:44:55",
.ip_addr = "192.168.1.1/24",
.speed = 1000,
.connected_to = "Ethernet",
.lease_time_left = 1000,
.last_seen = "13h20m ago"
},
{ .dev_name = "windows11",
.mac = "01:22:11:44:33:55",
.ip_addr = "192.168.1.2/24",
.speed = 200,
.connected_to = "Wifi 2.4 GHz",
.lease_time_left = 4141,
.last_seen = "23s ago"
},
{ .dev_name = "iRobot-2",
.mac = "01:22:11:44:33:42",
.ip_addr = "192.168.1.3/24",
.speed = 600,
.connected_to = "Wifi 5GHz",
.lease_time_left = 1141,
.last_seen = "20m ago"
}
};
// DHCP configuration
struct dhcp {
bool enabled;
@@ -184,6 +224,33 @@ static void handle_events_get(struct mg_connection *c) {
mg_http_reply(c, 200, s_json_header, "[%M]", print_events);
}
static void handle_devices_get(struct mg_connection *c) {
char test_json[1024];
int nr_devs = sizeof(s_devices) / sizeof(struct device);
memset(test_json, 0, sizeof(test_json));
test_json[0] = '[';
for (int i = 0; i < nr_devs; i++) {
size_t current_length = strlen(test_json);
mg_snprintf(test_json + current_length, sizeof(test_json) - current_length,
"{%m:\"%s\",%m:\"%s\", %m:\"%s\", %m:%d,%m:\"%s\",%m:%d,%m:\"%s\"}", //
MG_ESC("dev_name"), s_devices[i].dev_name, //
MG_ESC("mac"), s_devices[i].mac, //
MG_ESC("ip"), s_devices[i].ip_addr, //
MG_ESC("speed"), s_devices[i].speed, //
MG_ESC("connected_to"), s_devices[i].connected_to, //
MG_ESC("lease_time_left"), s_devices[i].lease_time_left, //
MG_ESC("last_seen"), s_devices[i].last_seen);
if (i < nr_devs - 1) {
strncat(test_json, ",", sizeof(test_json) - strlen(test_json) - 1);
}
}
strncat(test_json, "]", sizeof(test_json) - strlen(test_json) - 1);
mg_http_reply(c, 200, s_json_header, "%s", test_json);
}
static void handle_dhcp_set(struct mg_connection *c, struct mg_str body) {
struct dhcp dhcp;
memset(&dhcp, 0, sizeof(dhcp));
@@ -228,6 +295,8 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
handle_stats_get(c);
} else if (mg_http_match_uri(hm, "/api/events/get")) {
handle_events_get(c);
} else if (mg_http_match_uri(hm, "/api/devices/get")) {
handle_devices_get(c);
} else if (mg_http_match_uri(hm, "/api/dhcp/get")) {
handle_dhcp_get(c);
} else if (mg_http_match_uri(hm, "/api/dhcp/set")) {
@@ -1 +1 @@
/*! tailwindcss v3.3.2 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter var,Helvetica,sans-serif;font-feature-settings:"cv11","ss01";font-variation-settings:"opsz" 32}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.left-0{left:0}.right-auto{right:auto}.top-0{top:0}.z-10{z-index:10}.z-\[48\]{z-index:48}.z-\[60\]{z-index:60}.m-4{margin:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-0{margin-top:0;margin-bottom:0}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-5{margin-top:1.25rem;margin-bottom:1.25rem}.mb-1{margin-bottom:.25rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.mr-2{margin-right:.5rem}.mt-1{margin-top:.25rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.mt-7{margin-top:1.75rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-12{height:3rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-full{height:100%}.min-h-screen{min-height:100vh}.w-0{width:0}.w-11{width:2.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-72{width:18rem}.w-96{width:24rem}.w-full{width:100%}.min-w-full{min-width:100%}.max-w-\[85rem\]{max-width:85rem}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.border-separate{border-collapse:initial}.border-spacing-0{--tw-border-spacing-x:0px;--tw-border-spacing-y:0px;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.-translate-x-full{--tw-translate-x:-100%}.-translate-x-full,.translate-x-0{transform:translate(var(--tw-Line truncated
/*! tailwindcss v3.3.2 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter var,Helvetica,sans-serif;font-feature-settings:"cv11","ss01";font-variation-settings:"opsz" 32}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.left-0{left:0}.right-auto{right:auto}.top-0{top:0}.z-10{z-index:10}.z-\[48\]{z-index:48}.z-\[60\]{z-index:60}.m-4{margin:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-0{margin-top:0;margin-bottom:0}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-5{margin-top:1.25rem;margin-bottom:1.25rem}.mb-1{margin-bottom:.25rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.mr-2{margin-right:.5rem}.mt-1{margin-top:.25rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.mt-7{margin-top:1.75rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-12{height:3rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-full{height:100%}.min-h-screen{min-height:100vh}.w-0{width:0}.w-11{width:2.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-72{width:18rem}.w-96{width:24rem}.w-full{width:100%}.min-w-full{min-width:100%}.max-w-\[85rem\]{max-width:85rem}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.border-separate{border-collapse:initial}.border-spacing-0{--tw-border-spacing-x:0px;--tw-border-spacing-y:0px;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y)}.-translate-x-full{--tw-translate-x:-100%}.-translate-x-full,.translate-x-0{transform:translate(var(--tw-Line truncated
@@ -179,6 +179,60 @@ function Main({}) {
<//>`;
};
function Devices({}) {
const [devices, setDevices] = useState(null);
const refresh = () => fetch('api/devices/get')
.then(r => r.json())
.then(r => setDevices(r));
useEffect(refresh, []);
const th = props => html`<th scope="col" class="sticky top-0 z-10 border-b border-slate-300 bg-white bg-opacity-75 py-1.5 px-4 text-left text-sm font-semibold text-slate-900 backdrop-blur backdrop-filter">${props.title}</th>`;
const td = props => html`<td class="whitespace-nowrap border-b border-slate-200 py-2 px-4 pr-3 text-sm text-slate-900">${props.text}</td>`;
const Device = ({d}) => html`
<tr>
<${td} text=${d.dev_name} />
<${td} text=${d.mac} />
<${td} text=${d.ip} />
<${td} text=${d.speed} />
<${td} text=${d.connected_to} />
<${td} text=${d.lease_time_left} />
<${td} text=${d.last_seen} />
<//>`;
if (!devices) return '';
return html`
<div class="flex flex-col p-6 bg-gray-50 dark:bg-gray-900">
<div class="overflow-x-auto shadow-xl rounded-xl bg-white dark:bg-gray-800 p-6">
<div class="flex justify-end mb-4">
<${Button} title="Refresh" icon=${Icons.refresh} onclick=${refresh} />
</div>
<div class="align-middle inline-block w-full">
<div class="overflow-hidden">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700 text-sm md:text-base lg:text-lg">
<thead class="bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300">
<tr>
<${th} title="Device Name" />
<${th} title="MAC address" />
<${th} title="IP address" />
<${th} title="Speed (Mbps)" />
<${th} title="Connected to" />
<${th} title="Lease time left" />
<${th} title="Last seen" />
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-900 text-gray-800 dark:text-gray-200">
${devices.map(d => h(Device, {d}))}
</tbody>
</table>
</div>
</div>
</div>
</div>`;
};
function DHCP({}) {
const [dhcp, setDhcp] = useState(null);
const [saveResult, setSaveResult] = useState(null);
@@ -240,6 +294,7 @@ const App = function({}) {
<${Router} onChange=${ev => setUrl(ev.url)} history=${History.createHashHistory()} >
<${Main} default=${true} />
<${DHCP} path="dhcp" />
<${Devices} path="devices" />
<//>
<//>
<//>`;