|
|
|
@ -32,159 +32,134 @@ export const NodeDetailsDialog = ({ |
|
|
|
const { nodeNumDetails } = useAppStore(); |
|
|
|
const device: Protobuf.Mesh.NodeInfo = nodes.get(nodeNumDetails); |
|
|
|
|
|
|
|
return device |
|
|
|
? ( |
|
|
|
<Dialog open={open} onOpenChange={onOpenChange}> |
|
|
|
<DialogContent> |
|
|
|
<DialogHeader> |
|
|
|
<DialogTitle> |
|
|
|
Node Details for {device.user?.longName ?? "UNKNOWN"} ( |
|
|
|
{device.user?.shortName ?? "UNK"}) |
|
|
|
</DialogTitle> |
|
|
|
</DialogHeader> |
|
|
|
<DialogFooter> |
|
|
|
<div className="w-full"> |
|
|
|
<DeviceImage |
|
|
|
className="w-32 h-32 mx-auto rounded-lg border-4 border-slate-200 dark:border-slate-800" |
|
|
|
deviceType={Protobuf.Mesh |
|
|
|
.HardwareModel[device.user?.hwModel ?? 0]} |
|
|
|
/> |
|
|
|
<div className="bg-slate-100 dark:bg-slate-800 p-3 rounded-lg mt-3"> |
|
|
|
<p className="text-lg font-semibold text-slate-900 dark:text-slate-50"> |
|
|
|
Details: |
|
|
|
</p> |
|
|
|
<p> |
|
|
|
Hardware:{" "} |
|
|
|
{Protobuf.Mesh.HardwareModel[device.user?.hwModel ?? 0]} |
|
|
|
</p> |
|
|
|
<p>Node Number: {device.num}</p> |
|
|
|
<p>Node HEX: !{numberToHexUnpadded(device.num)}</p> |
|
|
|
<p> |
|
|
|
Role: {Protobuf.Config.Config_DeviceConfig_Role[ |
|
|
|
return device ? ( |
|
|
|
<Dialog open={open} onOpenChange={onOpenChange}> |
|
|
|
<DialogContent> |
|
|
|
<DialogHeader> |
|
|
|
<DialogTitle> |
|
|
|
Node Details for {device.user?.longName ?? "UNKNOWN"} ( |
|
|
|
{device.user?.shortName ?? "UNK"}) |
|
|
|
</DialogTitle> |
|
|
|
</DialogHeader> |
|
|
|
<DialogFooter> |
|
|
|
<div className="w-full"> |
|
|
|
<DeviceImage |
|
|
|
className="w-32 h-32 mx-auto rounded-lg border-4 border-slate-200 dark:border-slate-800" |
|
|
|
deviceType={ |
|
|
|
Protobuf.Mesh.HardwareModel[device.user?.hwModel ?? 0] |
|
|
|
} |
|
|
|
/> |
|
|
|
<div className="bg-slate-100 dark:bg-slate-800 p-3 rounded-lg mt-3"> |
|
|
|
<p className="text-lg font-semibold text-slate-900 dark:text-slate-50"> |
|
|
|
Details: |
|
|
|
</p> |
|
|
|
<p> |
|
|
|
Hardware:{" "} |
|
|
|
{Protobuf.Mesh.HardwareModel[device.user?.hwModel ?? 0]} |
|
|
|
</p> |
|
|
|
<p>Node Number: {device.num}</p> |
|
|
|
<p>Node HEX: !{numberToHexUnpadded(device.num)}</p> |
|
|
|
<p> |
|
|
|
Role:{" "} |
|
|
|
{ |
|
|
|
Protobuf.Config.Config_DeviceConfig_Role[ |
|
|
|
device.user?.role ?? 0 |
|
|
|
]} |
|
|
|
</p> |
|
|
|
<p> |
|
|
|
Last Heard: {device.lastHeard === 0 |
|
|
|
? ( |
|
|
|
"Never" |
|
|
|
) |
|
|
|
: <TimeAgo timestamp={device.lastHeard * 1000} />} |
|
|
|
] |
|
|
|
} |
|
|
|
</p> |
|
|
|
<p> |
|
|
|
Last Heard:{" "} |
|
|
|
{device.lastHeard === 0 ? ( |
|
|
|
"Never" |
|
|
|
) : ( |
|
|
|
<TimeAgo timestamp={device.lastHeard * 1000} /> |
|
|
|
)} |
|
|
|
</p> |
|
|
|
</div> |
|
|
|
|
|
|
|
{device.position ? ( |
|
|
|
<div className="mt-5 bg-slate-100 dark:bg-slate-800 p-3 rounded-lg mt-3"> |
|
|
|
<p className="text-lg font-semibold text-slate-900 dark:text-slate-50"> |
|
|
|
Position: |
|
|
|
</p> |
|
|
|
{device.position.latitudeI && device.position.longitudeI ? ( |
|
|
|
<p> |
|
|
|
Coordinates:{" "} |
|
|
|
<a |
|
|
|
className="text-blue-500 dark:text-blue-400" |
|
|
|
href={`https://www.openstreetmap.org/?mlat=${ |
|
|
|
device.position.latitudeI / 1e7 |
|
|
|
}&mlon=${device.position.longitudeI / 1e7}&layers=N`}
|
|
|
|
target="_blank" |
|
|
|
rel="noreferrer" |
|
|
|
> |
|
|
|
{device.position.latitudeI / 1e7},{" "} |
|
|
|
{device.position.longitudeI / 1e7} |
|
|
|
</a> |
|
|
|
</p> |
|
|
|
) : null} |
|
|
|
{device.position.altitude ? ( |
|
|
|
<p>Altitude: {device.position.altitude}m</p> |
|
|
|
) : null} |
|
|
|
</div> |
|
|
|
) : null} |
|
|
|
|
|
|
|
{device.position |
|
|
|
? ( |
|
|
|
<div className="mt-5 bg-slate-100 dark:bg-slate-800 p-3 rounded-lg mt-3"> |
|
|
|
<p className="text-lg font-semibold text-slate-900 dark:text-slate-50"> |
|
|
|
Position: |
|
|
|
</p> |
|
|
|
{device.position.latitudeI && device.position.longitudeI |
|
|
|
? ( |
|
|
|
<p> |
|
|
|
Coordinates:{" "} |
|
|
|
<a |
|
|
|
className="text-blue-500 dark:text-blue-400" |
|
|
|
href={`https://www.openstreetmap.org/?mlat=${ |
|
|
|
device.position.latitudeI / 1e7 |
|
|
|
}&mlon=${ |
|
|
|
device.position.longitudeI / 1e7 |
|
|
|
}&layers=N`}
|
|
|
|
target="_blank" |
|
|
|
rel="noreferrer" |
|
|
|
> |
|
|
|
{device.position.latitudeI / 1e7},{" "} |
|
|
|
{device.position.longitudeI / 1e7} |
|
|
|
</a> |
|
|
|
</p> |
|
|
|
) |
|
|
|
: null} |
|
|
|
{device.position.altitude |
|
|
|
? <p>Altitude: {device.position.altitude}m</p> |
|
|
|
: null} |
|
|
|
</div> |
|
|
|
) |
|
|
|
: null} |
|
|
|
|
|
|
|
{device.deviceMetrics |
|
|
|
? ( |
|
|
|
<div className="mt-5 bg-slate-100 dark:bg-slate-800 p-3 rounded-lg mt-3"> |
|
|
|
<p className="text-lg font-semibold text-slate-900 dark:text-slate-50"> |
|
|
|
Device Metrics: |
|
|
|
</p> |
|
|
|
{device.deviceMetrics.airUtilTx |
|
|
|
? ( |
|
|
|
<p> |
|
|
|
Air TX utilization:{" "} |
|
|
|
{device.deviceMetrics.airUtilTx.toFixed(2)}% |
|
|
|
</p> |
|
|
|
) |
|
|
|
: null} |
|
|
|
{device.deviceMetrics.channelUtilization |
|
|
|
? ( |
|
|
|
<p> |
|
|
|
Channel utilization:{" "} |
|
|
|
{device.deviceMetrics.channelUtilization.toFixed(2)}% |
|
|
|
</p> |
|
|
|
) |
|
|
|
: null} |
|
|
|
{device.deviceMetrics.batteryLevel |
|
|
|
? ( |
|
|
|
<p> |
|
|
|
Battery level:{" "} |
|
|
|
{device.deviceMetrics.batteryLevel.toFixed(2)}% |
|
|
|
</p> |
|
|
|
) |
|
|
|
: null} |
|
|
|
{device.deviceMetrics.voltage |
|
|
|
? ( |
|
|
|
<p> |
|
|
|
Voltage: {device.deviceMetrics.voltage.toFixed(2)}V |
|
|
|
</p> |
|
|
|
) |
|
|
|
: null} |
|
|
|
{device.deviceMetrics.uptimeSeconds |
|
|
|
? ( |
|
|
|
<p> |
|
|
|
Uptime:{" "} |
|
|
|
<Uptime |
|
|
|
seconds={device.deviceMetrics.uptimeSeconds} |
|
|
|
/> |
|
|
|
</p> |
|
|
|
) |
|
|
|
: null} |
|
|
|
</div> |
|
|
|
) |
|
|
|
: null} |
|
|
|
{device.deviceMetrics ? ( |
|
|
|
<div className="mt-5 bg-slate-100 dark:bg-slate-800 p-3 rounded-lg mt-3"> |
|
|
|
<p className="text-lg font-semibold text-slate-900 dark:text-slate-50"> |
|
|
|
Device Metrics: |
|
|
|
</p> |
|
|
|
{device.deviceMetrics.airUtilTx ? ( |
|
|
|
<p> |
|
|
|
Air TX utilization:{" "} |
|
|
|
{device.deviceMetrics.airUtilTx.toFixed(2)}% |
|
|
|
</p> |
|
|
|
) : null} |
|
|
|
{device.deviceMetrics.channelUtilization ? ( |
|
|
|
<p> |
|
|
|
Channel utilization:{" "} |
|
|
|
{device.deviceMetrics.channelUtilization.toFixed(2)}% |
|
|
|
</p> |
|
|
|
) : null} |
|
|
|
{device.deviceMetrics.batteryLevel ? ( |
|
|
|
<p> |
|
|
|
Battery level:{" "} |
|
|
|
{device.deviceMetrics.batteryLevel.toFixed(2)}% |
|
|
|
</p> |
|
|
|
) : null} |
|
|
|
{device.deviceMetrics.voltage ? ( |
|
|
|
<p>Voltage: {device.deviceMetrics.voltage.toFixed(2)}V</p> |
|
|
|
) : null} |
|
|
|
{device.deviceMetrics.uptimeSeconds ? ( |
|
|
|
<p> |
|
|
|
Uptime:{" "} |
|
|
|
<Uptime seconds={device.deviceMetrics.uptimeSeconds} /> |
|
|
|
</p> |
|
|
|
) : null} |
|
|
|
</div> |
|
|
|
) : null} |
|
|
|
|
|
|
|
{device |
|
|
|
? ( |
|
|
|
<div className="mt-5 w-full max-w-[464px] bg-slate-100 dark:bg-slate-800 p-3 rounded-lg mt-3"> |
|
|
|
<Accordion |
|
|
|
className="AccordionRoot" |
|
|
|
type="single" |
|
|
|
collapsible |
|
|
|
> |
|
|
|
<AccordionItem className="AccordionItem" value="item-1"> |
|
|
|
<AccordionTrigger> |
|
|
|
<p className="text-lg font-semibold text-slate-900 dark:text-slate-50"> |
|
|
|
All Raw Metrics: |
|
|
|
</p> |
|
|
|
</AccordionTrigger> |
|
|
|
<AccordionContent className="overflow-x-scroll"> |
|
|
|
<pre className="text-xs w-full"> |
|
|
|
{device ? ( |
|
|
|
<div className="mt-5 w-full max-w-[464px] bg-slate-100 dark:bg-slate-800 p-3 rounded-lg mt-3"> |
|
|
|
<Accordion className="AccordionRoot" type="single" collapsible> |
|
|
|
<AccordionItem className="AccordionItem" value="item-1"> |
|
|
|
<AccordionTrigger> |
|
|
|
<p className="text-lg font-semibold text-slate-900 dark:text-slate-50"> |
|
|
|
All Raw Metrics: |
|
|
|
</p> |
|
|
|
</AccordionTrigger> |
|
|
|
<AccordionContent className="overflow-x-scroll"> |
|
|
|
<pre className="text-xs w-full"> |
|
|
|
{JSON.stringify(device, null, 2)} |
|
|
|
</pre> |
|
|
|
</AccordionContent> |
|
|
|
</AccordionItem> |
|
|
|
</Accordion> |
|
|
|
</div> |
|
|
|
) |
|
|
|
: null} |
|
|
|
</div> |
|
|
|
</DialogFooter> |
|
|
|
</DialogContent> |
|
|
|
</Dialog> |
|
|
|
) |
|
|
|
: null; |
|
|
|
</pre> |
|
|
|
</AccordionContent> |
|
|
|
</AccordionItem> |
|
|
|
</Accordion> |
|
|
|
</div> |
|
|
|
) : null} |
|
|
|
</div> |
|
|
|
</DialogFooter> |
|
|
|
</DialogContent> |
|
|
|
</Dialog> |
|
|
|
) : null; |
|
|
|
}; |
|
|
|
|