Compare commits
43
Commits
pre-release
...
v2.5.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06d2c393ce | ||
|
|
cecdf9758b | ||
|
|
02cb4f2584 | ||
|
|
8cfcd7b1af | ||
|
|
c0cb059f52 | ||
|
|
a2a9b37238 | ||
|
|
57d0d27bbb | ||
|
|
0e92dd9bea | ||
|
|
c16ebf3917 | ||
|
|
3d3a08a23f | ||
|
|
4d1227a942 | ||
|
|
a8ee273b24 | ||
|
|
3ee7a57480 | ||
|
|
2f2c777c56 | ||
|
|
2f36118e9d | ||
|
|
a6d161581f | ||
|
|
d05ea5a2cc | ||
|
|
471db94242 | ||
|
|
2654e4fbc9 | ||
|
|
f2aa5bfbee | ||
|
|
3b018b0c70 | ||
|
|
921db10d91 | ||
|
|
bf4f593e3a | ||
|
|
1e061a1e19 | ||
|
|
9b9f537e2c | ||
|
|
985cce0b0d | ||
|
|
3fe38eb506 | ||
|
|
51081d3052 | ||
|
|
c08f6d16bb | ||
|
|
62ad4c49f8 | ||
|
|
3b0a1e6108 | ||
|
|
c2f2205626 | ||
|
|
87c729d694 | ||
|
|
8e4f60edf3 | ||
|
|
8811eee9f5 | ||
|
|
2af93f1acd | ||
|
|
78a35544c7 | ||
|
|
3ad2d650b0 | ||
|
|
bf425a8ec7 | ||
|
|
a7d0d36086 | ||
|
|
8ed3ce8203 | ||
|
|
ebd5a3d3a6 | ||
|
|
1cdf18747d |
@@ -27,6 +27,12 @@ jobs:
|
|||||||
- name: Package Output
|
- name: Package Output
|
||||||
run: pnpm package
|
run: pnpm package
|
||||||
|
|
||||||
|
- name: Archive compressed build
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: build
|
||||||
|
path: dist/build.tar
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -23,7 +23,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bufbuild/protobuf": "^1.10.0",
|
"@bufbuild/protobuf": "^1.10.0",
|
||||||
"@emeraldpay/hashicon-react": "^0.5.2",
|
"@emeraldpay/hashicon-react": "^0.5.2",
|
||||||
"@meshtastic/js": "2.3.7-1",
|
"@meshtastic/js": "2.3.7-5",
|
||||||
"@noble/curves": "^1.5.0",
|
"@noble/curves": "^1.5.0",
|
||||||
"@radix-ui/react-accordion": "^1.2.0",
|
"@radix-ui/react-accordion": "^1.2.0",
|
||||||
"@radix-ui/react-checkbox": "^1.1.0",
|
"@radix-ui/react-checkbox": "^1.1.0",
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
"crypto-random-string": "^5.0.0",
|
"crypto-random-string": "^5.0.0",
|
||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
"lucide-react": "^0.363.0",
|
"lucide-react": "^0.363.0",
|
||||||
"mapbox-gl": "npm:empty-npm-package@^1.0.0",
|
"mapbox-gl": "^3.6.0",
|
||||||
"maplibre-gl": "4.1.2",
|
"maplibre-gl": "4.1.2",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
@@ -60,11 +60,12 @@
|
|||||||
"tailwind-merge": "^2.3.0",
|
"tailwind-merge": "^2.3.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"timeago-react": "^3.0.6",
|
"timeago-react": "^3.0.6",
|
||||||
|
"vite-plugin-node-polyfills": "^0.22.0",
|
||||||
"zustand": "4.5.2"
|
"zustand": "4.5.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.8.2",
|
"@biomejs/biome": "^1.8.2",
|
||||||
"@buf/meshtastic_protobufs.bufbuild_es": "1.10.0-20240820152623-fac6975bbc78.1",
|
"@buf/meshtastic_protobufs.bufbuild_es": "1.10.0-20240906232734-3da561588c55.1",
|
||||||
"@types/chrome": "^0.0.263",
|
"@types/chrome": "^0.0.263",
|
||||||
"@types/node": "^20.14.9",
|
"@types/node": "^20.14.9",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
|
|||||||
Generated
+772
-20
File diff suppressed because it is too large
Load Diff
@@ -111,10 +111,14 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
|
|||||||
type: "select",
|
type: "select",
|
||||||
name: "role",
|
name: "role",
|
||||||
label: "Role",
|
label: "Role",
|
||||||
|
disabled: channel.index === 0,
|
||||||
description:
|
description:
|
||||||
"Device telemetry is sent over PRIMARY. Only one PRIMARY allowed",
|
"Device telemetry is sent over PRIMARY. Only one PRIMARY allowed",
|
||||||
properties: {
|
properties: {
|
||||||
enumValue: Protobuf.Channel.Channel_Role,
|
enumValue:
|
||||||
|
channel.index === 0
|
||||||
|
? { PRIMARY: 1 }
|
||||||
|
: { DISABLED: 0, SECONDARY: 2 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,7 +32,22 @@ export const Device = (): JSX.Element => {
|
|||||||
label: "Role",
|
label: "Role",
|
||||||
description: "What role the device performs on the mesh",
|
description: "What role the device performs on the mesh",
|
||||||
properties: {
|
properties: {
|
||||||
enumValue: Protobuf.Config.Config_DeviceConfig_Role,
|
enumValue: {
|
||||||
|
Client: Protobuf.Config.Config_DeviceConfig_Role.CLIENT,
|
||||||
|
"Client Mute":
|
||||||
|
Protobuf.Config.Config_DeviceConfig_Role.CLIENT_MUTE,
|
||||||
|
Router: Protobuf.Config.Config_DeviceConfig_Role.ROUTER,
|
||||||
|
Repeater: Protobuf.Config.Config_DeviceConfig_Role.REPEATER,
|
||||||
|
Tracker: Protobuf.Config.Config_DeviceConfig_Role.TRACKER,
|
||||||
|
Sensor: Protobuf.Config.Config_DeviceConfig_Role.SENSOR,
|
||||||
|
TAK: Protobuf.Config.Config_DeviceConfig_Role.TAK,
|
||||||
|
"Client Hidden":
|
||||||
|
Protobuf.Config.Config_DeviceConfig_Role.CLIENT_HIDDEN,
|
||||||
|
"Lost and Found":
|
||||||
|
Protobuf.Config.Config_DeviceConfig_Role.LOST_AND_FOUND,
|
||||||
|
"TAK Tracker":
|
||||||
|
Protobuf.Config.Config_DeviceConfig_Role.SENSOR,
|
||||||
|
},
|
||||||
formatEnumName: true,
|
formatEnumName: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -56,6 +56,13 @@ export const LoRa = (): JSX.Element => {
|
|||||||
label: "Ignore MQTT",
|
label: "Ignore MQTT",
|
||||||
description: "Don't forward MQTT messages over the mesh",
|
description: "Don't forward MQTT messages over the mesh",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "toggle",
|
||||||
|
name: "configOkToMqtt",
|
||||||
|
label: "OK to MQTT",
|
||||||
|
description:
|
||||||
|
"When set to true, this configuration indicates that the user approves the packet to be uploaded to MQTT. If set to false, remote nodes are requested not to forward packets to MQTT",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import type { NetworkValidation } from "@app/validation/config/network.js";
|
import type { NetworkValidation } from "@app/validation/config/network.js";
|
||||||
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
import { DynamicForm } from "@components/Form/DynamicForm.js";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.js";
|
||||||
|
import {
|
||||||
|
convertIntToIpAddress,
|
||||||
|
convertIpAddressToInt,
|
||||||
|
} from "@core/utils/ip.js";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
|
|
||||||
export const Network = (): JSX.Element => {
|
export const Network = (): JSX.Element => {
|
||||||
@@ -13,9 +17,12 @@ export const Network = (): JSX.Element => {
|
|||||||
case: "network",
|
case: "network",
|
||||||
value: {
|
value: {
|
||||||
...data,
|
...data,
|
||||||
ipv4Config: new Protobuf.Config.Config_NetworkConfig_IpV4Config(
|
ipv4Config: new Protobuf.Config.Config_NetworkConfig_IpV4Config({
|
||||||
data.ipv4Config,
|
ip: convertIpAddressToInt(data.ipv4Config.ip) ?? 0,
|
||||||
),
|
gateway: convertIpAddressToInt(data.ipv4Config.gateway) ?? 0,
|
||||||
|
subnet: convertIpAddressToInt(data.ipv4Config.subnet) ?? 0,
|
||||||
|
dns: convertIpAddressToInt(data.ipv4Config.dns) ?? 0,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@@ -25,7 +32,19 @@ export const Network = (): JSX.Element => {
|
|||||||
return (
|
return (
|
||||||
<DynamicForm<NetworkValidation>
|
<DynamicForm<NetworkValidation>
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
defaultValues={config.network}
|
defaultValues={{
|
||||||
|
...config.network,
|
||||||
|
ipv4Config: {
|
||||||
|
ip: convertIntToIpAddress(config.network?.ipv4Config?.ip ?? 0),
|
||||||
|
gateway: convertIntToIpAddress(
|
||||||
|
config.network?.ipv4Config?.gateway ?? 0,
|
||||||
|
),
|
||||||
|
subnet: convertIntToIpAddress(
|
||||||
|
config.network?.ipv4Config?.subnet ?? 0,
|
||||||
|
),
|
||||||
|
dns: convertIntToIpAddress(config.network?.ipv4Config?.dns ?? 0),
|
||||||
|
},
|
||||||
|
}}
|
||||||
fieldGroups={[
|
fieldGroups={[
|
||||||
{
|
{
|
||||||
label: "WiFi Config",
|
label: "WiFi Config",
|
||||||
|
|||||||
@@ -77,12 +77,6 @@ export const Position = (): JSX.Element => {
|
|||||||
label: "Enable Pin",
|
label: "Enable Pin",
|
||||||
description: "GPS module enable pin override",
|
description: "GPS module enable pin override",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: "number",
|
|
||||||
name: "channelPrecision",
|
|
||||||
label: "Channel Precision",
|
|
||||||
description: "GPS channel precision",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const Security = (): JSX.Element => {
|
|||||||
fromByteArray(config.security?.publicKey ?? new Uint8Array(0)),
|
fromByteArray(config.security?.publicKey ?? new Uint8Array(0)),
|
||||||
);
|
);
|
||||||
const [adminKey, setAdminKey] = useState<string>(
|
const [adminKey, setAdminKey] = useState<string>(
|
||||||
fromByteArray(config.security?.adminKey ?? new Uint8Array(0)),
|
fromByteArray(config.security?.adminKey[0] ?? new Uint8Array(0)),
|
||||||
);
|
);
|
||||||
const [adminKeyValidationText, setAdminKeyValidationText] =
|
const [adminKeyValidationText, setAdminKeyValidationText] =
|
||||||
useState<string>();
|
useState<string>();
|
||||||
@@ -42,7 +42,7 @@ export const Security = (): JSX.Element => {
|
|||||||
case: "security",
|
case: "security",
|
||||||
value: {
|
value: {
|
||||||
...data,
|
...data,
|
||||||
adminKey: toByteArray(adminKey),
|
adminKey: [toByteArray(adminKey)],
|
||||||
privateKey: toByteArray(privateKey),
|
privateKey: toByteArray(privateKey),
|
||||||
publicKey: toByteArray(publicKey),
|
publicKey: toByteArray(publicKey),
|
||||||
},
|
},
|
||||||
@@ -129,8 +129,6 @@ export const Security = (): JSX.Element => {
|
|||||||
publicKey: publicKey,
|
publicKey: publicKey,
|
||||||
adminChannelEnabled: config.security?.adminChannelEnabled ?? false,
|
adminChannelEnabled: config.security?.adminChannelEnabled ?? false,
|
||||||
isManaged: config.security?.isManaged ?? false,
|
isManaged: config.security?.isManaged ?? false,
|
||||||
bluetoothLoggingEnabled:
|
|
||||||
config.security?.bluetoothLoggingEnabled ?? false,
|
|
||||||
debugLogApiEnabled: config.security?.debugLogApiEnabled ?? false,
|
debugLogApiEnabled: config.security?.debugLogApiEnabled ?? false,
|
||||||
serialEnabled: config.security?.serialEnabled ?? false,
|
serialEnabled: config.security?.serialEnabled ?? false,
|
||||||
},
|
},
|
||||||
@@ -212,18 +210,12 @@ export const Security = (): JSX.Element => {
|
|||||||
label: "Logging Settings",
|
label: "Logging Settings",
|
||||||
description: "Settings for Logging",
|
description: "Settings for Logging",
|
||||||
fields: [
|
fields: [
|
||||||
{
|
|
||||||
type: "toggle",
|
|
||||||
name: "bluetoothLoggingEnabled",
|
|
||||||
label: "Allow Bluetooth Logging",
|
|
||||||
description:
|
|
||||||
"Enables device (serial style logs) over Bluetooth",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: "toggle",
|
type: "toggle",
|
||||||
name: "debugLogApiEnabled",
|
name: "debugLogApiEnabled",
|
||||||
label: "Enable Debug Log API",
|
label: "Enable Debug Log API",
|
||||||
description: "Output live debug logging over serial",
|
description:
|
||||||
|
"Output live debug logging over serial, view and export position-redacted device logs over Bluetooth",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "toggle",
|
type: "toggle",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useDevice } from "@app/core/stores/deviceStore.js";
|
import { useDevice } from "@app/core/stores/deviceStore.js";
|
||||||
import type { Protobuf } from "@meshtastic/js";
|
import type { Protobuf } from "@meshtastic/js";
|
||||||
|
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
||||||
|
|
||||||
export interface TraceRouteProps {
|
export interface TraceRouteProps {
|
||||||
from?: Protobuf.Mesh.NodeInfo;
|
from?: Protobuf.Mesh.NodeInfo;
|
||||||
@@ -24,7 +25,10 @@ export const TraceRoute = ({
|
|||||||
<div className="ml-5 flex">
|
<div className="ml-5 flex">
|
||||||
<span className="ml-4 border-l-2 border-l-backgroundPrimary pl-2 text-textPrimary">
|
<span className="ml-4 border-l-2 border-l-backgroundPrimary pl-2 text-textPrimary">
|
||||||
{to?.user?.longName}↔
|
{to?.user?.longName}↔
|
||||||
{route.map((hop) => `${nodes.get(hop)?.user?.longName ?? "Unknown"}↔`)}
|
{route.map((hop) => {
|
||||||
|
const node = nodes.get(hop);
|
||||||
|
return `${node?.user?.longName ?? (node?.num ? numberToHexUnpadded(node.num) : "Unknown")}↔`;
|
||||||
|
})}
|
||||||
{from?.user?.longName}
|
{from?.user?.longName}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export interface PageLayoutProps {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
actions?: {
|
actions?: {
|
||||||
icon: LucideIcon;
|
icon: LucideIcon;
|
||||||
|
iconClasses?: string;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
@@ -39,7 +40,7 @@ export const PageLayout = ({
|
|||||||
className="transition-all hover:text-accent"
|
className="transition-all hover:text-accent"
|
||||||
onClick={action.onClick}
|
onClick={action.onClick}
|
||||||
>
|
>
|
||||||
<action.icon />
|
<action.icon className={action.iconClasses} />
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
export function convertIntToIpAddress(int: number): string {
|
||||||
|
return `${int & 0xff}.${(int >> 8) & 0xff}.${(int >> 16) & 0xff}.${(int >> 24) & 0xff}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertIpAddressToInt(ip: string): number | null {
|
||||||
|
return (
|
||||||
|
ip
|
||||||
|
.split(".")
|
||||||
|
.reverse()
|
||||||
|
.reduce((ipnum, octet) => {
|
||||||
|
return (ipnum << 8) + Number.parseInt(octet);
|
||||||
|
}, 0) >>> 0
|
||||||
|
);
|
||||||
|
}
|
||||||
+3
-1
@@ -7,6 +7,7 @@ import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.js";
|
|||||||
import { useAppStore } from "@core/stores/appStore.js";
|
import { useAppStore } from "@core/stores/appStore.js";
|
||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.js";
|
||||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
|
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
||||||
import { bbox, lineString } from "@turf/turf";
|
import { bbox, lineString } from "@turf/turf";
|
||||||
import {
|
import {
|
||||||
BoxSelectIcon,
|
BoxSelectIcon,
|
||||||
@@ -180,7 +181,8 @@ export const MapPage = (): JSX.Element => {
|
|||||||
<div className="flex cursor-pointer gap-2 rounded-md border bg-backgroundPrimary p-1.5">
|
<div className="flex cursor-pointer gap-2 rounded-md border bg-backgroundPrimary p-1.5">
|
||||||
<Hashicon value={node.num.toString()} size={22} />
|
<Hashicon value={node.num.toString()} size={22} />
|
||||||
<Subtle className={cn(zoom < 12 && "hidden")}>
|
<Subtle className={cn(zoom < 12 && "hidden")}>
|
||||||
{node.user?.longName}
|
{node.user?.longName ||
|
||||||
|
`!${numberToHexUnpadded(node.num)}`}
|
||||||
</Subtle>
|
</Subtle>
|
||||||
</div>
|
</div>
|
||||||
</Marker>
|
</Marker>
|
||||||
|
|||||||
+23
-3
@@ -7,8 +7,9 @@ import { useToast } from "@core/hooks/useToast.js";
|
|||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.js";
|
||||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
import { Protobuf, Types } from "@meshtastic/js";
|
import { Protobuf, Types } from "@meshtastic/js";
|
||||||
|
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
||||||
import { getChannelName } from "@pages/Channels.js";
|
import { getChannelName } from "@pages/Channels.js";
|
||||||
import { HashIcon, WaypointsIcon } from "lucide-react";
|
import { HashIcon, LockIcon, LockOpenIcon, WaypointsIcon } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
export const MessagesPage = (): JSX.Element => {
|
export const MessagesPage = (): JSX.Element => {
|
||||||
@@ -28,6 +29,8 @@ export const MessagesPage = (): JSX.Element => {
|
|||||||
);
|
);
|
||||||
const currentChannel = channels.get(activeChat);
|
const currentChannel = channels.get(activeChat);
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
const node = nodes.get(activeChat);
|
||||||
|
const nodeHex = node?.num ? numberToHexUnpadded(node.num) : "Unknown";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -56,7 +59,7 @@ export const MessagesPage = (): JSX.Element => {
|
|||||||
{filteredNodes.map((node) => (
|
{filteredNodes.map((node) => (
|
||||||
<SidebarButton
|
<SidebarButton
|
||||||
key={node.num}
|
key={node.num}
|
||||||
label={node.user?.longName ?? "Unknown"}
|
label={node.user?.longName ?? `!${numberToHexUnpadded(node.num)}`}
|
||||||
active={activeChat === node.num}
|
active={activeChat === node.num}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setChatType("direct");
|
setChatType("direct");
|
||||||
@@ -73,12 +76,29 @@ export const MessagesPage = (): JSX.Element => {
|
|||||||
chatType === "broadcast" && currentChannel
|
chatType === "broadcast" && currentChannel
|
||||||
? getChannelName(currentChannel)
|
? getChannelName(currentChannel)
|
||||||
: chatType === "direct" && nodes.get(activeChat)
|
: chatType === "direct" && nodes.get(activeChat)
|
||||||
? nodes.get(activeChat)?.user?.longName ?? "Unknown"
|
? nodes.get(activeChat)?.user?.longName ?? nodeHex
|
||||||
: "Loading..."
|
: "Loading..."
|
||||||
}`}
|
}`}
|
||||||
actions={
|
actions={
|
||||||
chatType === "direct"
|
chatType === "direct"
|
||||||
? [
|
? [
|
||||||
|
{
|
||||||
|
icon: nodes.get(activeChat)?.user?.publicKey.length
|
||||||
|
? LockIcon
|
||||||
|
: LockOpenIcon,
|
||||||
|
iconClasses: nodes.get(activeChat)?.user?.publicKey.length
|
||||||
|
? "text-green-600"
|
||||||
|
: "text-yellow-300",
|
||||||
|
async onClick() {
|
||||||
|
const targetNode = nodes.get(activeChat)?.num;
|
||||||
|
if (targetNode === undefined) return;
|
||||||
|
toast({
|
||||||
|
title: nodes.get(activeChat)?.user?.publicKey.length
|
||||||
|
? "Chat is using PKI encryption."
|
||||||
|
: "Chat is using PSK encryption.",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: WaypointsIcon,
|
icon: WaypointsIcon,
|
||||||
async onClick() {
|
async onClick() {
|
||||||
|
|||||||
+11
-2
@@ -8,7 +8,8 @@ import { TimeAgo } from "@components/generic/Table/tmp/TimeAgo.js";
|
|||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.js";
|
||||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
import { Protobuf } from "@meshtastic/js";
|
import { Protobuf } from "@meshtastic/js";
|
||||||
import { TrashIcon } from "lucide-react";
|
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
|
||||||
|
import { LockIcon, LockOpenIcon, TrashIcon } from "lucide-react";
|
||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import { base16 } from "rfc4648";
|
import { base16 } from "rfc4648";
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ export const NodesPage = (): JSX.Element => {
|
|||||||
{ title: "MAC Address", type: "normal", sortable: true },
|
{ title: "MAC Address", type: "normal", sortable: true },
|
||||||
{ title: "Last Heard", type: "normal", sortable: true },
|
{ title: "Last Heard", type: "normal", sortable: true },
|
||||||
{ title: "SNR", type: "normal", sortable: true },
|
{ title: "SNR", type: "normal", sortable: true },
|
||||||
|
{ title: "Encryption", type: "normal", sortable: false },
|
||||||
{ title: "Connection", type: "normal", sortable: true },
|
{ title: "Connection", type: "normal", sortable: true },
|
||||||
{ title: "Remove", type: "normal", sortable: false },
|
{ title: "Remove", type: "normal", sortable: false },
|
||||||
]}
|
]}
|
||||||
@@ -49,7 +51,7 @@ export const NodesPage = (): JSX.Element => {
|
|||||||
? `Meshtastic ${base16
|
? `Meshtastic ${base16
|
||||||
.stringify(node.user?.macaddr.subarray(4, 6) ?? [])
|
.stringify(node.user?.macaddr.subarray(4, 6) ?? [])
|
||||||
.toLowerCase()}`
|
.toLowerCase()}`
|
||||||
: `UNK: ${node.num}`)}
|
: `!${numberToHexUnpadded(node.num)}`)}
|
||||||
</h1>,
|
</h1>,
|
||||||
|
|
||||||
<Mono key="model">
|
<Mono key="model">
|
||||||
@@ -73,6 +75,13 @@ export const NodesPage = (): JSX.Element => {
|
|||||||
{Math.min(Math.max((node.snr + 10) * 5, 0), 100)}%/
|
{Math.min(Math.max((node.snr + 10) * 5, 0), 100)}%/
|
||||||
{(node.snr + 10) * 5}raw
|
{(node.snr + 10) * 5}raw
|
||||||
</Mono>,
|
</Mono>,
|
||||||
|
<Mono key="pki">
|
||||||
|
{node.user?.publicKey && node.user?.publicKey.length > 0 ? (
|
||||||
|
<LockIcon className="text-green-600" />
|
||||||
|
) : (
|
||||||
|
<LockOpenIcon className="text-yellow-300" />
|
||||||
|
)}
|
||||||
|
</Mono>,
|
||||||
<Mono key="hops">
|
<Mono key="hops">
|
||||||
{node.lastHeard !== 0
|
{node.lastHeard !== 0
|
||||||
? node.viaMqtt === false && node.hopsAway === 0
|
? node.viaMqtt === false && node.hopsAway === 0
|
||||||
|
|||||||
@@ -60,4 +60,7 @@ export class LoRaValidation
|
|||||||
|
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
ignoreMqtt: boolean;
|
ignoreMqtt: boolean;
|
||||||
|
|
||||||
|
@IsBoolean()
|
||||||
|
configOkToMqtt: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,21 +41,24 @@ export class NetworkValidation
|
|||||||
|
|
||||||
export class NetworkValidationIpV4Config
|
export class NetworkValidationIpV4Config
|
||||||
implements
|
implements
|
||||||
Omit<Protobuf.Config.Config_NetworkConfig_IpV4Config, keyof Message>
|
Omit<
|
||||||
|
Protobuf.Config.Config_NetworkConfig_IpV4Config,
|
||||||
|
keyof Message | "ip" | "gateway" | "subnet" | "dns"
|
||||||
|
>
|
||||||
{
|
{
|
||||||
@IsIP()
|
@IsIP()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
ip: number;
|
ip: string;
|
||||||
|
|
||||||
@IsIP()
|
@IsIP()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
gateway: number;
|
gateway: string;
|
||||||
|
|
||||||
@IsIP()
|
@IsIP()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
subnet: number;
|
subnet: string;
|
||||||
|
|
||||||
@IsIP()
|
@IsIP()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
dns: number;
|
dns: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,4 @@ export class PositionValidation
|
|||||||
|
|
||||||
@IsEnum(Protobuf.Config.Config_PositionConfig_GpsMode)
|
@IsEnum(Protobuf.Config.Config_PositionConfig_GpsMode)
|
||||||
gpsMode: Protobuf.Config.Config_PositionConfig_GpsMode;
|
gpsMode: Protobuf.Config.Config_PositionConfig_GpsMode;
|
||||||
|
|
||||||
@IsArray()
|
|
||||||
channelPrecision: number[];
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -1,11 +1,10 @@
|
|||||||
import { execSync } from "node:child_process";
|
import { execSync } from "node:child_process";
|
||||||
import { resolve } from "node:path";
|
import { resolve } from "node:path";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
import { visualizer } from "rollup-plugin-visualizer";
|
import { visualizer } from "rollup-plugin-visualizer";
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import EnvironmentPlugin from "vite-plugin-environment";
|
import EnvironmentPlugin from "vite-plugin-environment";
|
||||||
|
|
||||||
import react from "@vitejs/plugin-react";
|
|
||||||
|
|
||||||
let hash = "";
|
let hash = "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user