import { Separator } from "@app/components/UI/Seperator";
import { Heading } from "@app/components/UI/Typography/Heading";
import { Subtle } from "@app/components/UI/Typography/Subtle.tsx";
import { formatQuantity } from "@app/core/utils/string";
import { Avatar } from "@components/UI/Avatar";
import { Mono } from "@components/generic/Mono.tsx";
import { TimeAgo } from "@components/generic/TimeAgo.tsx";
import { Protobuf } from "@meshtastic/js";
import type { Protobuf as ProtobufType } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
import {
BatteryChargingIcon,
BatteryFullIcon,
BatteryLowIcon,
BatteryMediumIcon,
Dot,
LockIcon,
LockOpenIcon,
MountainSnow,
Star,
} from "lucide-react";
export interface NodeDetailProps {
node: ProtobufType.Mesh.NodeInfo;
}
export const NodeDetail = ({ node }: NodeDetailProps) => {
const name = node.user?.longName || `!${numberToHexUnpadded(node.num)}`;
const hwModel = node.user?.hwModel ?? 0;
const hardwareType =
Protobuf.Mesh.HardwareModel[hwModel]?.replaceAll("_", " ") ?? `${hwModel}`;
return (
{node.user?.publicKey && node.user?.publicKey.length > 0 ? (
) : (
)}
{name}
{hardwareType !== "UNSET" &&
{hardwareType}}
{!!node.deviceMetrics?.batteryLevel && (
{node.deviceMetrics?.batteryLevel > 100 ? (
) : node.deviceMetrics?.batteryLevel > 80 ? (
) : node.deviceMetrics?.batteryLevel > 20 ? (
) : (
)}
{node.deviceMetrics?.batteryLevel > 100
? "Charging"
: `${node.deviceMetrics?.batteryLevel}%`}
)}
{node.user?.shortName &&
"{node.user?.shortName}"
}
{node.user?.id &&
{node.user?.id}
}
{node.lastHeard > 0 && (
Heard
)}
{node.viaMqtt && (
MQTT
)}
{Number.isNaN(node.hopsAway) ? "?" : node.hopsAway}
{node.hopsAway === 1 ? "Hop" : "Hops"}
{node.position?.altitude && (
{formatQuantity(node.position?.altitude, {
one: "meter",
other: "meters",
})}
)}
{!!node.deviceMetrics?.channelUtilization && (
Channel Util
{node.deviceMetrics?.channelUtilization.toPrecision(3)}%
)}
{!!node.deviceMetrics?.airUtilTx && (
Airtime Util
{node.deviceMetrics?.airUtilTx.toPrecision(3)}%
)}
{node.snr !== 0 && (
SNR
{node.snr}db
{Math.min(Math.max((node.snr + 10) * 5, 0), 100)}%
{(node.snr + 10) * 5}raw
)}
);
};