Browse Source

[chore] [sebfox] add intl & fr/en/it/de lngs

pull/326/head
SebFox2011 2 years ago
parent
commit
cef2f7abe4
  1. 4
      .vscode/settings.json
  2. 9433
      package-lock.json
  3. 3
      package.json
  4. 72
      src/components/CommandPalette.tsx
  5. 12
      src/components/Dialog/DeviceNameDialog.tsx
  6. 22
      src/components/Dialog/ImportDialog.tsx
  7. 34
      src/components/Dialog/NewDeviceDialog.tsx
  8. 8
      src/components/Dialog/PkiRegenerateDialog.tsx
  9. 22
      src/components/Dialog/QRDialog.tsx
  10. 9
      src/components/Dialog/RebootDialog.tsx
  11. 8
      src/components/Dialog/RemoveNodeDialog.tsx
  12. 8
      src/components/Dialog/ShutdownDialog.tsx
  13. 50
      src/components/PageComponents/Channel.tsx
  14. 22
      src/components/PageComponents/Config/Bluetooth.tsx
  15. 36
      src/components/PageComponents/Config/Device.tsx
  16. 48
      src/components/PageComponents/Config/Display.tsx
  17. 81
      src/components/PageComponents/Config/LoRa.tsx
  18. 68
      src/components/PageComponents/Config/Network.tsx
  19. 65
      src/components/PageComponents/Config/Position.tsx
  20. 59
      src/components/PageComponents/Config/Power.tsx
  21. 85
      src/components/PageComponents/Config/Security.tsx
  22. 8
      src/components/PageComponents/Connect/BLE.tsx
  23. 12
      src/components/PageComponents/Connect/HTTP.tsx
  24. 8
      src/components/PageComponents/Connect/Serial.tsx
  25. 10
      src/components/PageComponents/Messages/ChannelChat.tsx
  26. 12
      src/components/PageComponents/Messages/MessageInput.tsx
  27. 7
      src/components/PageComponents/Messages/TraceRoute.tsx
  28. 26
      src/components/PageComponents/ModuleConfig/AmbientLighting.tsx
  29. 36
      src/components/PageComponents/ModuleConfig/Audio.tsx
  30. 50
      src/components/PageComponents/ModuleConfig/CannedMessage.tsx
  31. 54
      src/components/PageComponents/ModuleConfig/DetectionSensor.tsx
  32. 69
      src/components/PageComponents/ModuleConfig/ExternalNotification.tsx
  33. 105
      src/components/PageComponents/ModuleConfig/MQTT.tsx
  34. 19
      src/components/PageComponents/ModuleConfig/NeighborInfo.tsx
  35. 17
      src/components/PageComponents/ModuleConfig/Paxcounter.tsx
  36. 20
      src/components/PageComponents/ModuleConfig/RangeTest.tsx
  37. 51
      src/components/PageComponents/ModuleConfig/Serial.tsx
  38. 28
      src/components/PageComponents/ModuleConfig/StoreForward.tsx
  39. 50
      src/components/PageComponents/ModuleConfig/Telemetry.tsx
  40. 12
      src/components/Sidebar.tsx
  41. 11
      src/components/UI/Footer.tsx
  42. 1
      src/index.tsx
  43. 4
      src/lang/de.d.ts
  44. 421
      src/lang/de.ts
  45. 4
      src/lang/en.d.ts
  46. 570
      src/lang/en.ts
  47. 4
      src/lang/fr.d.ts
  48. 600
      src/lang/fr.ts
  49. 28
      src/lang/i18n.ts
  50. 4
      src/lang/it.d.ts
  51. 596
      src/lang/it.ts
  52. 18
      src/pages/Config/DeviceConfig.tsx
  53. 26
      src/pages/Config/ModuleConfig.tsx
  54. 14
      src/pages/Config/index.tsx
  55. 18
      src/pages/Dashboard/index.tsx
  56. 24
      src/pages/Nodes.tsx

4
.vscode/settings.json

@ -3,5 +3,7 @@
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"quickfix.biome": "explicit" "quickfix.biome": "explicit"
}, },
"editor.formatOnSave": true "stm32-for-vscode.openOCDPath": false,
"stm32-for-vscode.makePath": false,
"stm32-for-vscode.armToolchainPath": false
} }

9433
package-lock.json

File diff suppressed because it is too large

3
package.json

@ -48,6 +48,8 @@
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "^1.0.0", "cmdk": "^1.0.0",
"crypto-random-string": "^5.0.0", "crypto-random-string": "^5.0.0",
"i18next": "^23.16.1",
"i18next-browser-languagedetector": "^8.0.0",
"immer": "^10.1.1", "immer": "^10.1.1",
"lucide-react": "^0.363.0", "lucide-react": "^0.363.0",
"mapbox-gl": "^3.6.0", "mapbox-gl": "^3.6.0",
@ -55,6 +57,7 @@
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-hook-form": "^7.52.0", "react-hook-form": "^7.52.0",
"react-i18next": "^15.0.3",
"react-map-gl": "7.1.7", "react-map-gl": "7.1.7",
"react-qrcode-logo": "^2.10.0", "react-qrcode-logo": "^2.10.0",
"rfc4648": "^1.5.3", "rfc4648": "^1.5.3",

72
src/components/CommandPalette.tsx

@ -10,6 +10,7 @@ import { useAppStore } from "@core/stores/appStore.ts";
import { useDevice, useDeviceStore } from "@core/stores/deviceStore.ts"; import { useDevice, useDeviceStore } from "@core/stores/deviceStore.ts";
import { Hashicon } from "@emeraldpay/hashicon-react"; import { Hashicon } from "@emeraldpay/hashicon-react";
import { useCommandState } from "cmdk"; import { useCommandState } from "cmdk";
import { useTranslation } from "react-i18next";
import { import {
ArrowLeftRightIcon, ArrowLeftRightIcon,
BoxSelectIcon, BoxSelectIcon,
@ -56,6 +57,7 @@ export interface SubItem {
} }
export const CommandPalette = (): JSX.Element => { export const CommandPalette = (): JSX.Element => {
const { t } = useTranslation();
const { const {
commandPaletteOpen, commandPaletteOpen,
setCommandPaletteOpen, setCommandPaletteOpen,
@ -71,25 +73,25 @@ export const CommandPalette = (): JSX.Element => {
const groups: Group[] = [ const groups: Group[] = [
{ {
label: "Goto", label: t("Goto"),
icon: LinkIcon, icon: LinkIcon,
commands: [ commands: [
{ {
label: "Messages", label: t("Messages"),
icon: MessageSquareIcon, icon: MessageSquareIcon,
action() { action() {
setActivePage("messages"); setActivePage("messages");
}, },
}, },
{ {
label: "Map", label: t("Map"),
icon: MapIcon, icon: MapIcon,
action() { action() {
setActivePage("map"); setActivePage("map");
}, },
}, },
{ {
label: "Config", label: t("Config"),
icon: SettingsIcon, icon: SettingsIcon,
action() { action() {
setActivePage("config"); setActivePage("config");
@ -97,14 +99,14 @@ export const CommandPalette = (): JSX.Element => {
tags: ["settings"], tags: ["settings"],
}, },
{ {
label: "Channels", label: t("Channels"),
icon: LayersIcon, icon: LayersIcon,
action() { action() {
setActivePage("channels"); setActivePage("channels");
}, },
}, },
{ {
label: "Nodes", label: t("Nodes"),
icon: UsersIcon, icon: UsersIcon,
action() { action() {
setActivePage("nodes"); setActivePage("nodes");
@ -113,11 +115,11 @@ export const CommandPalette = (): JSX.Element => {
], ],
}, },
{ {
label: "Manage", label: t("Manage"),
icon: SmartphoneIcon, icon: SmartphoneIcon,
commands: [ commands: [
{ {
label: "Switch Node", label: t("Switch Node"),
icon: ArrowLeftRightIcon, icon: ArrowLeftRightIcon,
subItems: getDevices().map((device) => { subItems: getDevices().map((device) => {
return { return {
@ -137,7 +139,7 @@ export const CommandPalette = (): JSX.Element => {
}), }),
}, },
{ {
label: "Connect New Node", label: t("Connect New Node"),
icon: PlusIcon, icon: PlusIcon,
action() { action() {
setSelectedDevice(0); setSelectedDevice(0);
@ -146,22 +148,22 @@ export const CommandPalette = (): JSX.Element => {
], ],
}, },
{ {
label: "Contextual", label: t("Contextual"),
icon: BoxSelectIcon, icon: BoxSelectIcon,
commands: [ commands: [
{ {
label: "QR Code", label: t("QR Code"),
icon: QrCodeIcon, icon: QrCodeIcon,
subItems: [ subItems: [
{ {
label: "Generator", label: t("Generator"),
icon: <QrCodeIcon size={16} />, icon: <QrCodeIcon size={16} />,
action() { action() {
setDialogOpen("QR", true); setDialogOpen("QR", true);
}, },
}, },
{ {
label: "Import", label: t("Import"),
icon: <QrCodeIcon size={16} />, icon: <QrCodeIcon size={16} />,
action() { action() {
setDialogOpen("import", true); setDialogOpen("import", true);
@ -170,7 +172,7 @@ export const CommandPalette = (): JSX.Element => {
], ],
}, },
{ {
label: "Disconnect", label: t("Disconnect"),
icon: XCircleIcon, icon: XCircleIcon,
action() { action() {
void connection?.disconnect(); void connection?.disconnect();
@ -179,35 +181,35 @@ export const CommandPalette = (): JSX.Element => {
}, },
}, },
{ {
label: "Schedule Shutdown", label: t("Schedule Shutdown"),
icon: PowerIcon, icon: PowerIcon,
action() { action() {
setDialogOpen("shutdown", true); setDialogOpen("shutdown", true);
}, },
}, },
{ {
label: "Schedule Reboot", label: t("Schedule Reboot"),
icon: RefreshCwIcon, icon: RefreshCwIcon,
action() { action() {
setDialogOpen("reboot", true); setDialogOpen("reboot", true);
}, },
}, },
{ {
label: "Reset Nodes", label: t("Reset Nodes"),
icon: TrashIcon, icon: TrashIcon,
action() { action() {
connection?.resetNodes(); connection?.resetNodes();
}, },
}, },
{ {
label: "Factory Reset Device", label: t("Factory Reset Device"),
icon: FactoryIcon, icon: FactoryIcon,
action() { action() {
connection?.factoryResetDevice(); connection?.factoryResetDevice();
}, },
}, },
{ {
label: "Factory Reset Config", label: t("Factory Reset Config"),
icon: FactoryIcon, icon: FactoryIcon,
action() { action() {
connection?.factoryResetConfig(); connection?.factoryResetConfig();
@ -216,42 +218,42 @@ export const CommandPalette = (): JSX.Element => {
], ],
}, },
{ {
label: "Debug", label: t("Debug"),
icon: BugIcon, icon: BugIcon,
commands: [ commands: [
{ {
label: "Reconfigure", label: t("Reconfigure"),
icon: RefreshCwIcon, icon: RefreshCwIcon,
action() { action() {
void connection?.configure(); void connection?.configure();
}, },
}, },
{ {
label: "[WIP] Clear Messages", label: t("[WIP] Clear Messages"),
icon: EraserIcon, icon: EraserIcon,
action() { action() {
alert("This feature is not implemented"); alert(t("This feature is not implemented"));
}, },
}, },
], ],
}, },
{ {
label: "Application", label: t("Application"),
icon: LayoutIcon, icon: LayoutIcon,
commands: [ commands: [
{ {
label: "Toggle Dark Mode", label: t("Toggle Dark Mode"),
icon: MoonIcon, icon: MoonIcon,
action() { action() {
setDarkMode(!darkMode); setDarkMode(!darkMode);
}, },
}, },
{ {
label: "Accent Color", label: t("Accent Color"),
icon: PaletteIcon, icon: PaletteIcon,
subItems: [ subItems: [
{ {
label: "Red", label: t("Red"),
icon: ( icon: (
<span <span
className={`h-3 w-3 rounded-full ${ className={`h-3 w-3 rounded-full ${
@ -264,7 +266,7 @@ export const CommandPalette = (): JSX.Element => {
}, },
}, },
{ {
label: "Orange", label: t("Orange"),
icon: ( icon: (
<span <span
className={`h-3 w-3 rounded-full ${ className={`h-3 w-3 rounded-full ${
@ -277,7 +279,7 @@ export const CommandPalette = (): JSX.Element => {
}, },
}, },
{ {
label: "Yellow", label: t("Yellow"),
icon: ( icon: (
<span <span
className={`h-3 w-3 rounded-full ${ className={`h-3 w-3 rounded-full ${
@ -290,7 +292,7 @@ export const CommandPalette = (): JSX.Element => {
}, },
}, },
{ {
label: "Green", label: t("Green"),
icon: ( icon: (
<span <span
className={`h-3 w-3 rounded-full ${ className={`h-3 w-3 rounded-full ${
@ -303,7 +305,7 @@ export const CommandPalette = (): JSX.Element => {
}, },
}, },
{ {
label: "Blue", label: t("Blue"),
icon: ( icon: (
<span <span
className={`h-3 w-3 rounded-full ${ className={`h-3 w-3 rounded-full ${
@ -316,7 +318,7 @@ export const CommandPalette = (): JSX.Element => {
}, },
}, },
{ {
label: "Purple", label: t("Purple"),
icon: ( icon: (
<span <span
className={`h-3 w-3 rounded-full ${ className={`h-3 w-3 rounded-full ${
@ -329,7 +331,7 @@ export const CommandPalette = (): JSX.Element => {
}, },
}, },
{ {
label: "Pink", label: t("Pink"),
icon: ( icon: (
<span <span
className={`h-3 w-3 rounded-full ${ className={`h-3 w-3 rounded-full ${
@ -364,9 +366,9 @@ export const CommandPalette = (): JSX.Element => {
open={commandPaletteOpen} open={commandPaletteOpen}
onOpenChange={setCommandPaletteOpen} onOpenChange={setCommandPaletteOpen}
> >
<CommandInput placeholder="Type a command or search..." /> <CommandInput placeholder={t("Type a command or search...")} />
<CommandList> <CommandList>
<CommandEmpty>No results found.</CommandEmpty> <CommandEmpty>{t("No results found.")}</CommandEmpty>
{groups.map((group) => ( {groups.map((group) => (
<CommandGroup key={group.label} heading={group.label}> <CommandGroup key={group.label} heading={group.label}>
{group.commands.map((command) => ( {group.commands.map((command) => (

12
src/components/Dialog/DeviceNameDialog.tsx

@ -12,6 +12,7 @@ import { Input } from "@components/UI/Input.tsx";
import { Label } from "@components/UI/Label.tsx"; import { Label } from "@components/UI/Label.tsx";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
export interface User { export interface User {
longName: string; longName: string;
@ -27,6 +28,7 @@ export const DeviceNameDialog = ({
open, open,
onOpenChange, onOpenChange,
}: DeviceNameDialogProps): JSX.Element => { }: DeviceNameDialogProps): JSX.Element => {
const { t } = useTranslation();
const { hardware, nodes, connection } = useDevice(); const { hardware, nodes, connection } = useDevice();
const myNode = nodes.get(hardware.myNodeNum); const myNode = nodes.get(hardware.myNodeNum);
@ -43,7 +45,7 @@ export const DeviceNameDialog = ({
new Protobuf.Mesh.User({ new Protobuf.Mesh.User({
...myNode?.user, ...myNode?.user,
...data, ...data,
}), })
); );
onOpenChange(false); onOpenChange(false);
}); });
@ -52,16 +54,16 @@ export const DeviceNameDialog = ({
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>Change Device Name</DialogTitle> <DialogTitle>{t("Change Device Name")}</DialogTitle>
<DialogDescription> <DialogDescription>
The Device will restart once the config is saved. {t("The Device will restart once the config is saved.")}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="gap-4"> <div className="gap-4">
<form onSubmit={onSubmit}> <form onSubmit={onSubmit}>
<Label>Long Name</Label> <Label>{t("Long Name")}</Label>
<Input {...register("longName")} /> <Input {...register("longName")} />
<Label>Short Name</Label> <Label>{t("Short Name")}</Label>
<Input maxLength={4} {...register("shortName")} /> <Input maxLength={4} {...register("shortName")} />
</form> </form>
</div> </div>

22
src/components/Dialog/ImportDialog.tsx

@ -15,6 +15,7 @@ import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
import { toByteArray } from "base64-js"; import { toByteArray } from "base64-js";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
export interface ImportDialogProps { export interface ImportDialogProps {
open: boolean; open: boolean;
@ -26,6 +27,7 @@ export const ImportDialog = ({
open, open,
onOpenChange, onOpenChange,
}: ImportDialogProps): JSX.Element => { }: ImportDialogProps): JSX.Element => {
const { t } = useTranslation();
const [importDialogInput, setImportDialogInput] = useState<string>(""); const [importDialogInput, setImportDialogInput] = useState<string>("");
const [channelSet, setChannelSet] = useState<Protobuf.AppOnly.ChannelSet>(); const [channelSet, setChannelSet] = useState<Protobuf.AppOnly.ChannelSet>();
const [validUrl, setValidUrl] = useState<boolean>(false); const [validUrl, setValidUrl] = useState<boolean>(false);
@ -50,12 +52,12 @@ export const ImportDialog = ({
.padEnd( .padEnd(
encodedChannelConfig.length + encodedChannelConfig.length +
((4 - (encodedChannelConfig.length % 4)) % 4), ((4 - (encodedChannelConfig.length % 4)) % 4),
"=", "="
) )
.replace(/-/g, "+") .replace(/-/g, "+")
.replace(/_/g, "/"); .replace(/_/g, "/");
setChannelSet( setChannelSet(
Protobuf.AppOnly.ChannelSet.fromBinary(toByteArray(paddedString)), Protobuf.AppOnly.ChannelSet.fromBinary(toByteArray(paddedString))
); );
setValidUrl(true); setValidUrl(true);
} catch (error) { } catch (error) {
@ -74,7 +76,7 @@ export const ImportDialog = ({
? Protobuf.Channel.Channel_Role.PRIMARY ? Protobuf.Channel.Channel_Role.PRIMARY
: Protobuf.Channel.Channel_Role.SECONDARY, : Protobuf.Channel.Channel_Role.SECONDARY,
settings: ch, settings: ch,
}), })
); );
}); });
@ -85,7 +87,7 @@ export const ImportDialog = ({
case: "lora", case: "lora",
value: channelSet.loraConfig, value: channelSet.loraConfig,
}, },
}), })
); );
} }
}; };
@ -94,13 +96,13 @@ export const ImportDialog = ({
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>Import Channel Set</DialogTitle> <DialogTitle>{t("Import Channel Set")}</DialogTitle>
<DialogDescription> <DialogDescription>
The current LoRa configuration will be overridden. {t("The current LoRa configuration will be overridden.")}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<Label>Channel Set/QR Code URL</Label> <Label>{t("Channel Set/QR Code URL")}</Label>
<Input <Input
value={importDialogInput} value={importDialogInput}
suffix={validUrl ? "✅" : "❌"} suffix={validUrl ? "✅" : "❌"}
@ -112,7 +114,7 @@ export const ImportDialog = ({
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<div className="flex w-full gap-2"> <div className="flex w-full gap-2">
<div className="w-36"> <div className="w-36">
<Label>Use Preset?</Label> <Label>{t("Use Preset?")}</Label>
<Switch <Switch
disabled={true} disabled={true}
checked={channelSet?.loraConfig?.usePreset ?? true} checked={channelSet?.loraConfig?.usePreset ?? true}
@ -135,7 +137,7 @@ export const ImportDialog = ({
</Select> */} </Select> */}
<span className="text-md block font-medium text-textPrimary"> <span className="text-md block font-medium text-textPrimary">
Channels: {t("Channels:")}
</span> </span>
<div className="flex w-40 flex-col gap-1"> <div className="flex w-40 flex-col gap-1">
{channelSet?.settings.map((channel) => ( {channelSet?.settings.map((channel) => (
@ -154,7 +156,7 @@ export const ImportDialog = ({
</div> </div>
<DialogFooter> <DialogFooter>
<Button onClick={apply} disabled={!validUrl}> <Button onClick={apply} disabled={!validUrl}>
Apply {t("Apply")}
</Button> </Button>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>

34
src/components/Dialog/NewDeviceDialog.tsx

@ -15,6 +15,8 @@ import {
} from "@components/UI/Tabs.tsx"; } from "@components/UI/Tabs.tsx";
import { Link } from "@components/UI/Typography/Link.tsx"; import { Link } from "@components/UI/Typography/Link.tsx";
import { Subtle } from "@components/UI/Typography/Subtle.tsx"; import { Subtle } from "@components/UI/Typography/Subtle.tsx";
import { useTranslation } from "react-i18next";
import i18n from "i18next";
export interface TabElementProps { export interface TabElementProps {
closeDialog: () => void; closeDialog: () => void;
@ -30,26 +32,28 @@ export interface TabManifest {
const tabs: TabManifest[] = [ const tabs: TabManifest[] = [
{ {
label: "HTTP", label: i18n.t("HTTP"),
element: HTTP, element: HTTP,
disabled: false, disabled: false,
disabledMessage: "Unsuported connection method", disabledMessage: i18n.t("Unsuported connection method"),
}, },
{ {
label: "Bluetooth", label: i18n.t("Bluetooth"),
element: BLE, element: BLE,
disabled: !navigator.bluetooth, disabled: !navigator.bluetooth,
disabledMessage: disabledMessage: i18n.t(
"Web Bluetooth is currently only supported by Chromium-based browsers", "Web Bluetooth is currently only supported by Chromium-based browsers"
),
disabledLink: disabledLink:
"https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API#browser_compatibility", "https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API#browser_compatibility",
}, },
{ {
label: "Serial", label: i18n.t("Serial"),
element: Serial, element: Serial,
disabled: !navigator.serial, disabled: !navigator.serial,
disabledMessage: disabledMessage: i18n.t(
"WebSerial is currently only supported by Chromium based browsers: https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API#browser_compatibility", "WebSerial is currently only supported by Chromium based browsers"
),
}, },
]; ];
export interface NewDeviceProps { export interface NewDeviceProps {
@ -61,11 +65,12 @@ export const NewDeviceDialog = ({
open, open,
onOpenChange, onOpenChange,
}: NewDeviceProps): JSX.Element => { }: NewDeviceProps): JSX.Element => {
const { t } = useTranslation();
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>Connect New Device</DialogTitle> <DialogTitle>{t("Connect New Device")}</DialogTitle>
</DialogHeader> </DialogHeader>
<Tabs defaultValue="HTTP"> <Tabs defaultValue="HTTP">
<TabsList> <TabsList>
@ -95,17 +100,18 @@ export const NewDeviceDialog = ({
{(!navigator.bluetooth || !navigator.serial) && ( {(!navigator.bluetooth || !navigator.serial) && (
<> <>
<Subtle> <Subtle>
Web Bluetooth and Web Serial are currently only supported by {t(
Chromium-based browsers. "Web Bluetooth and Web Serial are currently only supported by Chromium-based browsers."
)}
</Subtle> </Subtle>
<Subtle> <Subtle>
Read more:&nbsp; {t("Read more:")}
<Link href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API#browser_compatibility"> <Link href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API#browser_compatibility">
Web Bluetooth {t("Web Bluetooth")}
</Link> </Link>
&nbsp; &nbsp;
<Link href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API#browser_compatibility"> <Link href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API#browser_compatibility">
Web Serial {t("Web Serial")}
</Link> </Link>
</Subtle> </Subtle>
</> </>

8
src/components/Dialog/PkiRegenerateDialog.tsx

@ -7,6 +7,7 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from "@components/UI/Dialog.tsx"; } from "@components/UI/Dialog.tsx";
import { useTranslation } from "react-i18next";
export interface PkiRegenerateDialogProps { export interface PkiRegenerateDialogProps {
open: boolean; open: boolean;
@ -19,18 +20,19 @@ export const PkiRegenerateDialog = ({
onOpenChange, onOpenChange,
onSubmit, onSubmit,
}: PkiRegenerateDialogProps): JSX.Element => { }: PkiRegenerateDialogProps): JSX.Element => {
const { t } = useTranslation();
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>Regenerate Key pair?</DialogTitle> <DialogTitle>{t("Regenerate Key pair?")}</DialogTitle>
<DialogDescription> <DialogDescription>
Are you sure you want to regenerate key pair? {t("Are you sure you want to regenerate key pair?")}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<DialogFooter> <DialogFooter>
<Button variant="destructive" onClick={() => onSubmit()}> <Button variant="destructive" onClick={() => onSubmit()}>
Regenerate {t("Regenerate")}
</Button> </Button>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>

22
src/components/Dialog/QRDialog.tsx

@ -14,6 +14,7 @@ import { fromByteArray } from "base64-js";
import { ClipboardIcon } from "lucide-react"; import { ClipboardIcon } from "lucide-react";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { QRCode } from "react-qrcode-logo"; import { QRCode } from "react-qrcode-logo";
import { useTranslation } from "react-i18next";
export interface QRDialogProps { export interface QRDialogProps {
open: boolean; open: boolean;
@ -28,6 +29,7 @@ export const QRDialog = ({
loraConfig, loraConfig,
channels, channels,
}: QRDialogProps): JSX.Element => { }: QRDialogProps): JSX.Element => {
const { t } = useTranslation();
const [selectedChannels, setSelectedChannels] = useState<number[]>([0]); const [selectedChannels, setSelectedChannels] = useState<number[]>([0]);
const [qrCodeUrl, setQrCodeUrl] = useState<string>(""); const [qrCodeUrl, setQrCodeUrl] = useState<string>("");
const [qrCodeAdd, setQrCodeAdd] = useState<boolean>(); const [qrCodeAdd, setQrCodeAdd] = useState<boolean>();
@ -43,7 +45,7 @@ export const QRDialog = ({
new Protobuf.AppOnly.ChannelSet({ new Protobuf.AppOnly.ChannelSet({
loraConfig, loraConfig,
settings: channelsToEncode, settings: channelsToEncode,
}), })
); );
const base64 = fromByteArray(encoded.toBinary()) const base64 = fromByteArray(encoded.toBinary())
.replace(/=/g, "") .replace(/=/g, "")
@ -51,7 +53,7 @@ export const QRDialog = ({
.replace(/\//g, "_"); .replace(/\//g, "_");
setQrCodeUrl( setQrCodeUrl(
`https://meshtastic.org/e/${qrCodeAdd ? "?add=true" : ""}#${base64}`, `https://meshtastic.org/e/${qrCodeAdd ? "?add=true" : ""}#${base64}`
); );
}, [allChannels, selectedChannels, qrCodeAdd, loraConfig]); }, [allChannels, selectedChannels, qrCodeAdd, loraConfig]);
@ -59,9 +61,9 @@ export const QRDialog = ({
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>Generate QR Code</DialogTitle> <DialogTitle>{t("Generate QR Code")}</DialogTitle>
<DialogDescription> <DialogDescription>
The current LoRa configuration will also be shared. {t("The current LoRa configuration will also be shared.")}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="grid gap-4 py-4"> <div className="grid gap-4 py-4">
@ -73,8 +75,8 @@ export const QRDialog = ({
{channel.settings?.name.length {channel.settings?.name.length
? channel.settings.name ? channel.settings.name
: channel.role === Protobuf.Channel.Channel_Role.PRIMARY : channel.role === Protobuf.Channel.Channel_Role.PRIMARY
? "Primary" ? "Primary"
: `Channel: ${channel.index}`} : `Channel: ${channel.index}`}
</Label> </Label>
<Checkbox <Checkbox
key={channel.index} key={channel.index}
@ -82,7 +84,7 @@ export const QRDialog = ({
onCheckedChange={() => { onCheckedChange={() => {
if (selectedChannels.includes(channel.index)) { if (selectedChannels.includes(channel.index)) {
setSelectedChannels( setSelectedChannels(
selectedChannels.filter((c) => c !== channel.index), selectedChannels.filter((c) => c !== channel.index)
); );
} else { } else {
setSelectedChannels([ setSelectedChannels([
@ -107,7 +109,7 @@ export const QRDialog = ({
}`} }`}
onClick={() => setQrCodeAdd(true)} onClick={() => setQrCodeAdd(true)}
> >
Add Channels {t("Add Channels")}
</button> </button>
<button <button
type="button" type="button"
@ -118,12 +120,12 @@ export const QRDialog = ({
}`} }`}
onClick={() => setQrCodeAdd(false)} onClick={() => setQrCodeAdd(false)}
> >
Replace Channels {t("Replace Channels")}
</button> </button>
</div> </div>
</div> </div>
<DialogFooter> <DialogFooter>
<Label>Sharable URL</Label> <Label>{t("Sharable URL")}</Label>
<Input <Input
value={qrCodeUrl} value={qrCodeUrl}
disabled={true} disabled={true}

9
src/components/Dialog/RebootDialog.tsx

@ -10,7 +10,7 @@ import { Input } from "@components/UI/Input.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { ClockIcon, RefreshCwIcon } from "lucide-react"; import { ClockIcon, RefreshCwIcon } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next";
export interface RebootDialogProps { export interface RebootDialogProps {
open: boolean; open: boolean;
onOpenChange: (open: boolean) => void; onOpenChange: (open: boolean) => void;
@ -20,6 +20,7 @@ export const RebootDialog = ({
open, open,
onOpenChange, onOpenChange,
}: RebootDialogProps): JSX.Element => { }: RebootDialogProps): JSX.Element => {
const { t } = useTranslation();
const { connection } = useDevice(); const { connection } = useDevice();
const [time, setTime] = useState<number>(5); const [time, setTime] = useState<number>(5);
@ -28,9 +29,9 @@ export const RebootDialog = ({
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>Schedule Reboot</DialogTitle> <DialogTitle>{t("Schedule Reboot")}</DialogTitle>
<DialogDescription> <DialogDescription>
Reboot the connected node after x minutes. {t("Reboot the connected node after x minutes.")}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="flex gap-2 p-4"> <div className="flex gap-2 p-4">
@ -52,7 +53,7 @@ export const RebootDialog = ({
}} }}
> >
<RefreshCwIcon className="mr-2" size={16} /> <RefreshCwIcon className="mr-2" size={16} />
Now {t("Now")}
</Button> </Button>
</div> </div>
</DialogContent> </DialogContent>

8
src/components/Dialog/RemoveNodeDialog.tsx

@ -10,6 +10,7 @@ import {
DialogTitle, DialogTitle,
} from "@components/UI/Dialog.tsx"; } from "@components/UI/Dialog.tsx";
import { Label } from "@components/UI/Label.tsx"; import { Label } from "@components/UI/Label.tsx";
import { useTranslation } from "react-i18next";
export interface RemoveNodeDialogProps { export interface RemoveNodeDialogProps {
open: boolean; open: boolean;
@ -20,6 +21,7 @@ export const RemoveNodeDialog = ({
open, open,
onOpenChange, onOpenChange,
}: RemoveNodeDialogProps): JSX.Element => { }: RemoveNodeDialogProps): JSX.Element => {
const { t } = useTranslation();
const { connection, nodes, removeNode } = useDevice(); const { connection, nodes, removeNode } = useDevice();
const { nodeNumToBeRemoved } = useAppStore(); const { nodeNumToBeRemoved } = useAppStore();
@ -33,9 +35,9 @@ export const RemoveNodeDialog = ({
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>Remove Node?</DialogTitle> <DialogTitle>{t("Remove Node?")}</DialogTitle>
<DialogDescription> <DialogDescription>
Are you sure you want to remove this Node? {t("Are you sure you want to remove this Node?")}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="gap-4"> <div className="gap-4">
@ -45,7 +47,7 @@ export const RemoveNodeDialog = ({
</div> </div>
<DialogFooter> <DialogFooter>
<Button variant="destructive" onClick={() => onSubmit()}> <Button variant="destructive" onClick={() => onSubmit()}>
Remove {t("Remove")}
</Button> </Button>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>

8
src/components/Dialog/ShutdownDialog.tsx

@ -10,6 +10,7 @@ import { Input } from "@components/UI/Input.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { ClockIcon, PowerIcon } from "lucide-react"; import { ClockIcon, PowerIcon } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next";
export interface ShutdownDialogProps { export interface ShutdownDialogProps {
open: boolean; open: boolean;
@ -20,6 +21,7 @@ export const ShutdownDialog = ({
open, open,
onOpenChange, onOpenChange,
}: ShutdownDialogProps): JSX.Element => { }: ShutdownDialogProps): JSX.Element => {
const { t } = useTranslation();
const { connection } = useDevice(); const { connection } = useDevice();
const [time, setTime] = useState<number>(5); const [time, setTime] = useState<number>(5);
@ -28,9 +30,9 @@ export const ShutdownDialog = ({
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>Schedule Shutdown</DialogTitle> <DialogTitle>{t("Schedule Shutdown")}</DialogTitle>
<DialogDescription> <DialogDescription>
Turn off the connected node after x minutes. {t("Turn off the connected node after x minutes.")}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@ -56,7 +58,7 @@ export const ShutdownDialog = ({
}} }}
> >
<PowerIcon className="mr-2" size={16} /> <PowerIcon className="mr-2" size={16} />
Now {t("Now")}
</Button> </Button>
</div> </div>
</DialogContent> </DialogContent>

50
src/components/PageComponents/Channel.tsx

@ -5,6 +5,7 @@ import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
import { fromByteArray, toByteArray } from "base64-js"; import { fromByteArray, toByteArray } from "base64-js";
import cryptoRandomString from "crypto-random-string"; import cryptoRandomString from "crypto-random-string";
import { useTranslation } from "react-i18next";
import { useState } from "react"; import { useState } from "react";
export interface SettingsPanelProps { export interface SettingsPanelProps {
@ -14,12 +15,13 @@ export interface SettingsPanelProps {
export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
const { config, connection, addChannel } = useDevice(); const { config, connection, addChannel } = useDevice();
const { toast } = useToast(); const { toast } = useToast();
const { t } = useTranslation();
const [pass, setPass] = useState<string>( const [pass, setPass] = useState<string>(
fromByteArray(channel?.settings?.psk ?? new Uint8Array(0)), fromByteArray(channel?.settings?.psk ?? new Uint8Array(0))
); );
const [bitCount, setBits] = useState<number>( const [bitCount, setBits] = useState<number>(
channel?.settings?.psk.length ?? 16, channel?.settings?.psk.length ?? 16
); );
const [validationText, setValidationText] = useState<string>(); const [validationText, setValidationText] = useState<string>();
@ -52,8 +54,8 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
cryptoRandomString({ cryptoRandomString({
length: bitCount ?? 0, length: bitCount ?? 0,
type: "alphanumeric", type: "alphanumeric",
}), })
), )
); );
setValidationText(undefined); setValidationText(undefined);
}; };
@ -104,16 +106,17 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
}} }}
fieldGroups={[ fieldGroups={[
{ {
label: "Channel Settings", label: t("Channel Settings"),
description: "Crypto, MQTT & misc settings", description: t("Crypto, MQTT & misc settings"),
fields: [ fields: [
{ {
type: "select", type: "select",
name: "role", name: "role",
label: "Role", label: t("Role"),
disabled: channel.index === 0, disabled: channel.index === 0,
description: description: t(
"Device telemetry is sent over PRIMARY. Only one PRIMARY allowed", "Device telemetry is sent over PRIMARY. Only one PRIMARY allowed"
),
properties: { properties: {
enumValue: enumValue:
channel.index === 0 channel.index === 0
@ -124,8 +127,8 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
{ {
type: "passwordGenerator", type: "passwordGenerator",
name: "settings.psk", name: "settings.psk",
label: "pre-Shared Key", label: t("pre-Shared Key"),
description: "256, 128, or 8 bit PSKs allowed", description: t("256, 128, or 8 bit PSKs allowed"),
validationText: validationText, validationText: validationText,
devicePSKBitCount: bitCount ?? 0, devicePSKBitCount: bitCount ?? 0,
inputChange: inputChangeEvent, inputChange: inputChangeEvent,
@ -139,40 +142,41 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
{ {
type: "text", type: "text",
name: "settings.name", name: "settings.name",
label: "Name", label: t("Name"),
description: description: t(
"A unique name for the channel <12 bytes, leave blank for default", "A unique name for the channel <12 bytes, leave blank for default"
),
}, },
{ {
type: "toggle", type: "toggle",
name: "settings.uplinkEnabled", name: "settings.uplinkEnabled",
label: "Uplink Enabled", label: t("Uplink Enabled"),
description: "Send messages from the local mesh to MQTT", description: "Send messages from the local mesh to MQTT",
}, },
{ {
type: "toggle", type: "toggle",
name: "settings.downlinkEnabled", name: "settings.downlinkEnabled",
label: "Downlink Enabled", label: t("Downlink Enabled"),
description: "Send messages from MQTT to the local mesh", description: t("Send messages from MQTT to the local mesh"),
}, },
{ {
type: "toggle", type: "toggle",
name: "settings.positionEnabled", name: "settings.positionEnabled",
label: "Allow Position Requests", label: t("Allow Position Requests"),
description: "Send position to channel", description: t("Send position to channel"),
}, },
{ {
type: "toggle", type: "toggle",
name: "settings.preciseLocation", name: "settings.preciseLocation",
label: "Precise Location", label: t("Precise Location"),
description: "Send precise location to channel", description: t("Send precise location to channel"),
}, },
{ {
type: "select", type: "select",
name: "settings.positionPrecision", name: "settings.positionPrecision",
label: "Approximate Location", label: t("Approximate Location"),
description: description:
"If not sharing precise location, position shared on channel will be accurate within this distance", t("If not sharing precise location, position shared on channel will be accurate within this distance"),
properties: { properties: {
enumValue: enumValue:
config.display?.units === 0 config.display?.units === 0

22
src/components/PageComponents/Config/Bluetooth.tsx

@ -3,15 +3,17 @@ import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next";
export const Bluetooth = (): JSX.Element => { export const Bluetooth = (): JSX.Element => {
const { config, setWorkingConfig } = useDevice(); const { config, setWorkingConfig } = useDevice();
const { t } = useTranslation();
const [bluetoothValidationText, setBluetoothValidationText] = useState<string>(); const [bluetoothValidationText, setBluetoothValidationText] = useState<string>();
const bluetoothPinChangeEvent = ( const bluetoothPinChangeEvent = (
e: React.ChangeEvent<HTMLInputElement>, e: React.ChangeEvent<HTMLInputElement>,
) => { ) => {
if (e.target.value[0] == "0") if (e.target.value[0] === "0")
{ {
setBluetoothValidationText("Bluetooth Pin cannot start with 0."); setBluetoothValidationText("Bluetooth Pin cannot start with 0.");
} }
@ -28,7 +30,7 @@ export const Bluetooth = (): JSX.Element => {
case: "bluetooth", case: "bluetooth",
value: data, value: data,
}, },
}), })
); );
}; };
@ -38,20 +40,20 @@ export const Bluetooth = (): JSX.Element => {
defaultValues={config.bluetooth} defaultValues={config.bluetooth}
fieldGroups={[ fieldGroups={[
{ {
label: "Bluetooth Settings", label: t("Bluetooth Settings"),
description: "Settings for the Bluetooth module", description: t("Settings for the Bluetooth module"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "enabled", name: "enabled",
label: "Enabled", label: t("Enabled"),
description: "Enable or disable Bluetooth", description: t("Enable or disable Bluetooth"),
}, },
{ {
type: "select", type: "select",
name: "mode", name: "mode",
label: "Pairing mode", label: t("Pairing mode"),
description: "Pin selection behaviour.", description: t("Pin selection behaviour."),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -65,10 +67,10 @@ export const Bluetooth = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "fixedPin", name: "fixedPin",
label: "Pin",
description: "Pin to use when pairing",
validationText: bluetoothValidationText, validationText: bluetoothValidationText,
inputChange: bluetoothPinChangeEvent, inputChange: bluetoothPinChangeEvent,
label:t( "Pin"),
description: t("Pin to use when pairing"),
disabledBy: [ disabledBy: [
{ {
fieldName: "mode", fieldName: "mode",

36
src/components/PageComponents/Config/Device.tsx

@ -2,9 +2,11 @@ import type { DeviceValidation } from "@app/validation/config/device.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
import { useTranslation } from "react-i18next";
export const Device = (): JSX.Element => { export const Device = (): JSX.Element => {
const { config, setWorkingConfig } = useDevice(); const { config, setWorkingConfig } = useDevice();
const { t } = useTranslation();
const onSubmit = (data: DeviceValidation) => { const onSubmit = (data: DeviceValidation) => {
setWorkingConfig( setWorkingConfig(
@ -13,7 +15,7 @@ export const Device = (): JSX.Element => {
case: "device", case: "device",
value: data, value: data,
}, },
}), })
); );
}; };
@ -23,14 +25,14 @@ export const Device = (): JSX.Element => {
defaultValues={config.device} defaultValues={config.device}
fieldGroups={[ fieldGroups={[
{ {
label: "Device Settings", label: t("Device Settings"),
description: "Settings for the device", description: t("Settings for the device"),
fields: [ fields: [
{ {
type: "select", type: "select",
name: "role", name: "role",
label: "Role", label: t("Role"),
description: "What role the device performs on the mesh", description: t("What role the device performs on the mesh"),
properties: { properties: {
enumValue: { enumValue: {
Client: Protobuf.Config.Config_DeviceConfig_Role.CLIENT, Client: Protobuf.Config.Config_DeviceConfig_Role.CLIENT,
@ -54,20 +56,20 @@ export const Device = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "buttonGpio", name: "buttonGpio",
label: "Button Pin", label: t("Button Pin"),
description: "Button pin override", description: t("Button pin override"),
}, },
{ {
type: "number", type: "number",
name: "buzzerGpio", name: "buzzerGpio",
label: "Buzzer Pin", label: t("Buzzer Pin"),
description: "Buzzer pin override", description: t("Buzzer pin override"),
}, },
{ {
type: "select", type: "select",
name: "rebroadcastMode", name: "rebroadcastMode",
label: "Rebroadcast Mode", label: t("Rebroadcast Mode"),
description: "How to handle rebroadcasting", description: t("How to handle rebroadcasting"),
properties: { properties: {
enumValue: Protobuf.Config.Config_DeviceConfig_RebroadcastMode, enumValue: Protobuf.Config.Config_DeviceConfig_RebroadcastMode,
formatEnumName: true, formatEnumName: true,
@ -76,8 +78,8 @@ export const Device = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "nodeInfoBroadcastSecs", name: "nodeInfoBroadcastSecs",
label: "Node Info Broadcast Interval", label: t("Node Info Broadcast Interval"),
description: "How often to broadcast node info", description: t("How often to broadcast node info"),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },
@ -85,14 +87,14 @@ export const Device = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "doubleTapAsButtonPress", name: "doubleTapAsButtonPress",
label: "Double Tap as Button Press", label: t("Double Tap as Button Press"),
description: "Treat double tap as button press", description: t("Treat double tap as button press"),
}, },
{ {
type: "toggle", type: "toggle",
name: "disableTripleClick", name: "disableTripleClick",
label: "Disable Triple Click", label: t("Disable Triple Click"),
description: "Disable triple click", description: t("Disable Triple Click"),
}, },
{ {
type: "toggle", type: "toggle",

48
src/components/PageComponents/Config/Display.tsx

@ -2,9 +2,11 @@ import type { DisplayValidation } from "@app/validation/config/display.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
import { useTranslation } from "react-i18next";
export const Display = (): JSX.Element => { export const Display = (): JSX.Element => {
const { config, setWorkingConfig } = useDevice(); const { config, setWorkingConfig } = useDevice();
const { t } = useTranslation();
const onSubmit = (data: DisplayValidation) => { const onSubmit = (data: DisplayValidation) => {
setWorkingConfig( setWorkingConfig(
@ -13,7 +15,7 @@ export const Display = (): JSX.Element => {
case: "display", case: "display",
value: data, value: data,
}, },
}), })
); );
}; };
@ -23,14 +25,14 @@ export const Display = (): JSX.Element => {
defaultValues={config.display} defaultValues={config.display}
fieldGroups={[ fieldGroups={[
{ {
label: "Display Settings", label: t("Display Settings"),
description: "Settings for the device display", description: t("Settings for the device display"),
fields: [ fields: [
{ {
type: "number", type: "number",
name: "screenOnSecs", name: "screenOnSecs",
label: "Screen Timeout", label: t("Screen Timeout"),
description: "Turn off the display after this long", description: t("Turn off the display after this long"),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },
@ -38,8 +40,8 @@ export const Display = (): JSX.Element => {
{ {
type: "select", type: "select",
name: "gpsFormat", name: "gpsFormat",
label: "GPS Display Units", label: t("GPS Display Units"),
description: "Coordinate display format", description: t("Coordinate display format"),
properties: { properties: {
enumValue: enumValue:
Protobuf.Config.Config_DisplayConfig_GpsCoordinateFormat, Protobuf.Config.Config_DisplayConfig_GpsCoordinateFormat,
@ -48,8 +50,8 @@ export const Display = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "autoScreenCarouselSecs", name: "autoScreenCarouselSecs",
label: "Carousel Delay", label: t("Carousel Delay"),
description: "How fast to cycle through windows", description: t("How fast to cycle through windows"),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },
@ -57,20 +59,20 @@ export const Display = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "compassNorthTop", name: "compassNorthTop",
label: "Compass North Top", label: t("Compass North Top"),
description: "Fix north to the top of compass", description: t("Fix north to the top of compass"),
}, },
{ {
type: "toggle", type: "toggle",
name: "flipScreen", name: "flipScreen",
label: "Flip Screen", label: t("Flip Screen"),
description: "Flip display 180 degrees", description: t("Flip display 180 degrees"),
}, },
{ {
type: "select", type: "select",
name: "units", name: "units",
label: "Display Units", label: t("Display Units"),
description: "Display metric or imperial units", description: t("Display metric or imperial units"),
properties: { properties: {
enumValue: Protobuf.Config.Config_DisplayConfig_DisplayUnits, enumValue: Protobuf.Config.Config_DisplayConfig_DisplayUnits,
formatEnumName: true, formatEnumName: true,
@ -79,8 +81,8 @@ export const Display = (): JSX.Element => {
{ {
type: "select", type: "select",
name: "oled", name: "oled",
label: "OLED Type", label: t("OLED Type"),
description: "Type of OLED screen attached to the device", description: t("Type of OLED screen attached to the device"),
properties: { properties: {
enumValue: Protobuf.Config.Config_DisplayConfig_OledType, enumValue: Protobuf.Config.Config_DisplayConfig_OledType,
}, },
@ -88,8 +90,8 @@ export const Display = (): JSX.Element => {
{ {
type: "select", type: "select",
name: "displaymode", name: "displaymode",
label: "Display Mode", label: t("Display Mode"),
description: "Screen layout variant", description: t("Screen layout variant"),
properties: { properties: {
enumValue: Protobuf.Config.Config_DisplayConfig_DisplayMode, enumValue: Protobuf.Config.Config_DisplayConfig_DisplayMode,
formatEnumName: true, formatEnumName: true,
@ -98,14 +100,14 @@ export const Display = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "headingBold", name: "headingBold",
label: "Bold Heading", label: t("Bold Heading"),
description: "Bolden the heading text", description: t("Bolden the heading text"),
}, },
{ {
type: "toggle", type: "toggle",
name: "wakeOnTapOrMotion", name: "wakeOnTapOrMotion",
label: "Wake on Tap or Motion", label: t("Wake on Tap or Motion"),
description: "Wake the device on tap or motion", description: t("Wake the device on tap or motion"),
}, },
], ],
}, },

81
src/components/PageComponents/Config/LoRa.tsx

@ -1,10 +1,12 @@
import type { LoRaValidation } from "@app/validation/config/lora.tsx"; import type { LoRaValidation } from "@app/validation/config/lora.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { useTranslation } from "react-i18next";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
export const LoRa = (): JSX.Element => { export const LoRa = (): JSX.Element => {
const { config, setWorkingConfig } = useDevice(); const { config, setWorkingConfig } = useDevice();
const { t } = useTranslation();
const onSubmit = (data: LoRaValidation) => { const onSubmit = (data: LoRaValidation) => {
setWorkingConfig( setWorkingConfig(
@ -13,7 +15,7 @@ export const LoRa = (): JSX.Element => {
case: "lora", case: "lora",
value: data, value: data,
}, },
}), })
); );
}; };
@ -23,14 +25,14 @@ export const LoRa = (): JSX.Element => {
defaultValues={config.lora} defaultValues={config.lora}
fieldGroups={[ fieldGroups={[
{ {
label: "Mesh Settings", label: t("Mesh Settings"),
description: "Settings for the LoRa mesh", description: t("Settings for the LoRa mesh"),
fields: [ fields: [
{ {
type: "select", type: "select",
name: "region", name: "region",
label: "Region", label: t("Region"),
description: "Sets the region for your node", description: t("Sets the region for your node"),
properties: { properties: {
enumValue: Protobuf.Config.Config_LoRaConfig_RegionCode, enumValue: Protobuf.Config.Config_LoRaConfig_RegionCode,
}, },
@ -38,8 +40,8 @@ export const LoRa = (): JSX.Element => {
{ {
type: "select", type: "select",
name: "hopLimit", name: "hopLimit",
label: "Hop Limit", label: t("Hop Limit"),
description: "Maximum number of hops", description: t("Maximum number of hops"),
properties: { properties: {
enumValue: { 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7 }, enumValue: { 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7 },
}, },
@ -47,39 +49,38 @@ export const LoRa = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "channelNum", name: "channelNum",
label: "Frequency Slot", label: t("Frequency Slot"),
description: "LoRa frequency channel number", description: t("LoRa frequency channel number"),
}, },
{ {
type: "toggle", type: "toggle",
name: "ignoreMqtt", name: "ignoreMqtt",
label: "Ignore MQTT", label: t("Ignore MQTT"),
description: "Don't forward MQTT messages over the mesh", description: t("Don't forward MQTT messages over the mesh"),
}, },
{ {
type: "toggle", type: "toggle",
name: "configOkToMqtt", name: "configOkToMqtt",
label: "OK to MQTT", label: t("OK to MQTT"),
description: description: t("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",
}, },
], ],
}, },
{ {
label: "Waveform Settings", label: t("Waveform Settings"),
description: "Settings for the LoRa waveform", description: t("Settings for the LoRa waveform"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "usePreset", name: "usePreset",
label: "Use Preset", label: t("Use Preset"),
description: "Use one of the predefined modem presets", description: t("Use one of the predefined modem presets"),
}, },
{ {
type: "select", type: "select",
name: "modemPreset", name: "modemPreset",
label: "Modem Preset", label:t( "Modem Preset"),
description: "Modem preset to use", description:t( "Modem preset to use"),
disabledBy: [ disabledBy: [
{ {
fieldName: "usePreset", fieldName: "usePreset",
@ -93,8 +94,8 @@ export const LoRa = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "bandwidth", name: "bandwidth",
label: "Bandwidth", label: t("Bandwidth"),
description: "Channel bandwidth in MHz", description:t( "Channel bandwidth in MHz"),
disabledBy: [ disabledBy: [
{ {
fieldName: "usePreset", fieldName: "usePreset",
@ -108,8 +109,8 @@ export const LoRa = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "spreadFactor", name: "spreadFactor",
label: "Spreading Factor", label: t("Spreading Factor"),
description: "Indicates the number of chirps per symbol", description: ("Indicates the number of chirps per symbol"),
disabledBy: [ disabledBy: [
{ {
@ -124,8 +125,8 @@ export const LoRa = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "codingRate", name: "codingRate",
label: "Coding Rate", label: t("Coding Rate"),
description: "The denominator of the coding rate", description: t("The denominator of the coding rate"),
disabledBy: [ disabledBy: [
{ {
fieldName: "usePreset", fieldName: "usePreset",
@ -136,20 +137,20 @@ export const LoRa = (): JSX.Element => {
], ],
}, },
{ {
label: "Radio Settings", label: t("Radio Settings"),
description: "Settings for the LoRa radio", description: t("Settings for the LoRa radio"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "txEnabled", name: "txEnabled",
label: "Transmit Enabled", label:t( "Transmit Enabled"),
description: "Enable/Disable transmit (TX) from the LoRa radio", description: t("Enable/Disable transmit (TX) from the LoRa radio"),
}, },
{ {
type: "number", type: "number",
name: "txPower", name: "txPower",
label: "Transmit Power", label: t("Transmit Power"),
description: "Max transmit power", description: t("Max transmit power"),
properties: { properties: {
suffix: "dBm", suffix: "dBm",
}, },
@ -157,15 +158,15 @@ export const LoRa = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "overrideDutyCycle", name: "overrideDutyCycle",
label: "Override Duty Cycle", label: t("Override Duty Cycle"),
description: "Override Duty Cycle", description: t("Override Duty Cycle"),
}, },
{ {
type: "number", type: "number",
name: "frequencyOffset", name: "frequencyOffset",
label: "Frequency Offset", label: t("Frequency Offset"),
description: description:
"Frequency offset to correct for crystal calibration errors", t("Frequency offset to correct for crystal calibration errors"),
properties: { properties: {
suffix: "Hz", suffix: "Hz",
}, },
@ -173,14 +174,14 @@ export const LoRa = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "sx126xRxBoostedGain", name: "sx126xRxBoostedGain",
label: "Boosted RX Gain", label: t("Boosted RX Gain"),
description: "Boosted RX gain", description: t("Boosted RX gain"),
}, },
{ {
type: "number", type: "number",
name: "overrideFrequency", name: "overrideFrequency",
label: "Override Frequency", label: t("Override Frequency"),
description: "Override frequency", description: t("Override frequency"),
properties: { properties: {
suffix: "MHz", suffix: "MHz",
step: 0.001, step: 0.001,

68
src/components/PageComponents/Config/Network.tsx

@ -1,6 +1,7 @@
import type { NetworkValidation } from "@app/validation/config/network.tsx"; import type { NetworkValidation } from "@app/validation/config/network.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { useTranslation } from "react-i18next";
import { import {
convertIntToIpAddress, convertIntToIpAddress,
convertIpAddressToInt, convertIpAddressToInt,
@ -9,6 +10,7 @@ import { Protobuf } from "@meshtastic/js";
export const Network = (): JSX.Element => { export const Network = (): JSX.Element => {
const { config, setWorkingConfig } = useDevice(); const { config, setWorkingConfig } = useDevice();
const { t } = useTranslation();
const onSubmit = (data: NetworkValidation) => { const onSubmit = (data: NetworkValidation) => {
setWorkingConfig( setWorkingConfig(
@ -25,7 +27,7 @@ export const Network = (): JSX.Element => {
}), }),
}, },
}, },
}), })
); );
}; };
@ -37,30 +39,30 @@ export const Network = (): JSX.Element => {
ipv4Config: { ipv4Config: {
ip: convertIntToIpAddress(config.network?.ipv4Config?.ip ?? 0), ip: convertIntToIpAddress(config.network?.ipv4Config?.ip ?? 0),
gateway: convertIntToIpAddress( gateway: convertIntToIpAddress(
config.network?.ipv4Config?.gateway ?? 0, config.network?.ipv4Config?.gateway ?? 0
), ),
subnet: convertIntToIpAddress( subnet: convertIntToIpAddress(
config.network?.ipv4Config?.subnet ?? 0, config.network?.ipv4Config?.subnet ?? 0
), ),
dns: convertIntToIpAddress(config.network?.ipv4Config?.dns ?? 0), dns: convertIntToIpAddress(config.network?.ipv4Config?.dns ?? 0),
}, },
}} }}
fieldGroups={[ fieldGroups={[
{ {
label: "WiFi Config", label: t("WiFi Config"),
description: "WiFi radio configuration", description: t("WiFi radio configuration"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "wifiEnabled", name: "wifiEnabled",
label: "Enabled", label: t("Enabled"),
description: "Enable or disable the WiFi radio", description: t("Enable or disable the WiFi radio"),
}, },
{ {
type: "text", type: "text",
name: "wifiSsid", name: "wifiSsid",
label: "SSID", label: t("SSID"),
description: "Network name", description: t("Network name"),
disabledBy: [ disabledBy: [
{ {
fieldName: "wifiEnabled", fieldName: "wifiEnabled",
@ -70,8 +72,8 @@ export const Network = (): JSX.Element => {
{ {
type: "password", type: "password",
name: "wifiPsk", name: "wifiPsk",
label: "PSK", label: t("PSK"),
description: "Network password", description: t("Network password"),
disabledBy: [ disabledBy: [
{ {
fieldName: "wifiEnabled", fieldName: "wifiEnabled",
@ -81,26 +83,26 @@ export const Network = (): JSX.Element => {
], ],
}, },
{ {
label: "Ethernet Config", label: t("Ethernet Config"),
description: "Ethernet port configuration", description: t("Ethernet port configuration"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "ethEnabled", name: "ethEnabled",
label: "Enabled", label: t("Enabled"),
description: "Enable or disable the Ethernet port", description: t("Enable or disable the Ethernet port"),
}, },
], ],
}, },
{ {
label: "IP Config", label: t("IP Config"),
description: "IP configuration", description: t("IP configuration"),
fields: [ fields: [
{ {
type: "select", type: "select",
name: "addressMode", name: "addressMode",
label: "Address Mode", label: t("Address Mode"),
description: "Address assignment selection", description: t("Address assignment selection"),
properties: { properties: {
enumValue: Protobuf.Config.Config_NetworkConfig_AddressMode, enumValue: Protobuf.Config.Config_NetworkConfig_AddressMode,
}, },
@ -108,8 +110,8 @@ export const Network = (): JSX.Element => {
{ {
type: "text", type: "text",
name: "ipv4Config.ip", name: "ipv4Config.ip",
label: "IP", label: t("IP"),
description: "IP Address", description: t("IP Address"),
disabledBy: [ disabledBy: [
{ {
fieldName: "addressMode", fieldName: "addressMode",
@ -121,8 +123,8 @@ export const Network = (): JSX.Element => {
{ {
type: "text", type: "text",
name: "ipv4Config.gateway", name: "ipv4Config.gateway",
label: "Gateway", label: t("Gateway"),
description: "Default Gateway", description: t("Default Gateway"),
disabledBy: [ disabledBy: [
{ {
fieldName: "addressMode", fieldName: "addressMode",
@ -134,8 +136,8 @@ export const Network = (): JSX.Element => {
{ {
type: "text", type: "text",
name: "ipv4Config.subnet", name: "ipv4Config.subnet",
label: "Subnet", label: t("Subnet"),
description: "Subnet Mask", description: t("Subnet Mask"),
disabledBy: [ disabledBy: [
{ {
fieldName: "addressMode", fieldName: "addressMode",
@ -147,8 +149,8 @@ export const Network = (): JSX.Element => {
{ {
type: "text", type: "text",
name: "ipv4Config.dns", name: "ipv4Config.dns",
label: "DNS", label: t("DNS"),
description: "DNS Server", description: t("DNS Server"),
disabledBy: [ disabledBy: [
{ {
fieldName: "addressMode", fieldName: "addressMode",
@ -160,24 +162,24 @@ export const Network = (): JSX.Element => {
], ],
}, },
{ {
label: "NTP Config", label: t("NTP Config"),
description: "NTP configuration", description: t("NTP configuration"),
fields: [ fields: [
{ {
type: "text", type: "text",
name: "ntpServer", name: "ntpServer",
label: "NTP Server", label: t("NTP Server"),
}, },
], ],
}, },
{ {
label: "Rsyslog Config", label: t("Rsyslog Config"),
description: "Rsyslog configuration", description: t("Rsyslog configuration"),
fields: [ fields: [
{ {
type: "text", type: "text",
name: "rsyslogServer", name: "rsyslogServer",
label: "Rsyslog Server", label: t("Rsyslog Server"),
}, },
], ],
}, },

65
src/components/PageComponents/Config/Position.tsx

@ -1,10 +1,12 @@
import type { PositionValidation } from "@app/validation/config/position.tsx"; import type { PositionValidation } from "@app/validation/config/position.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { useTranslation } from "react-i18next";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
export const Position = (): JSX.Element => { export const Position = (): JSX.Element => {
const { config, nodes, hardware, setWorkingConfig } = useDevice(); const { config, nodes, hardware, setWorkingConfig } = useDevice();
const { t } = useTranslation();
const onSubmit = (data: PositionValidation) => { const onSubmit = (data: PositionValidation) => {
setWorkingConfig( setWorkingConfig(
@ -13,7 +15,7 @@ export const Position = (): JSX.Element => {
case: "position", case: "position",
value: data, value: data,
}, },
}), })
); );
}; };
@ -23,22 +25,24 @@ export const Position = (): JSX.Element => {
defaultValues={config.position} defaultValues={config.position}
fieldGroups={[ fieldGroups={[
{ {
label: "Position Settings", label: t("Position Settings"),
description: "Settings for the position module", description: t("Settings for the position module"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "positionBroadcastSmartEnabled", name: "positionBroadcastSmartEnabled",
label: "Enable Smart Position", label: t("Enable Smart Position"),
description: description: t(
"Only send position when there has been a meaningful change in location", "Only send position when there has been a meaningful change in location"
),
}, },
{ {
type: "select", type: "select",
name: "gpsMode", name: "gpsMode",
label: "GPS Mode", label: t("GPS Mode"),
description: description: t(
"Configure whether device GPS is Enabled, Disabled, or Not Present", "Configure whether device GPS is Enabled, Disabled, or Not Present"
),
properties: { properties: {
enumValue: Protobuf.Config.Config_PositionConfig_GpsMode, enumValue: Protobuf.Config.Config_PositionConfig_GpsMode,
}, },
@ -46,15 +50,16 @@ export const Position = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "fixedPosition", name: "fixedPosition",
label: "Fixed Position", label: t("Fixed Position"),
description: description: t(
"Don't report GPS position, but a manually-specified one", "Don't report GPS position, but a manually-specified one"
),
}, },
{ {
type: "multiSelect", type: "multiSelect",
name: "positionFlags", name: "positionFlags",
label: "Position Flags", label: t("Position Flags"),
description: "Configuration options for Position messages", description: t("Configuration options for Position messages"),
properties: { properties: {
enumValue: Protobuf.Config.Config_PositionConfig_PositionFlags, enumValue: Protobuf.Config.Config_PositionConfig_PositionFlags,
}, },
@ -62,32 +67,32 @@ export const Position = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "rxGpio", name: "rxGpio",
label: "Receive Pin", label: t("Receive Pin"),
description: "GPS module RX pin override", description: t("GPS module RX pin override"),
}, },
{ {
type: "number", type: "number",
name: "txGpio", name: "txGpio",
label: "Transmit Pin", label: t("Transmit Pin"),
description: "GPS module TX pin override", description: t("GPS module TX pin override"),
}, },
{ {
type: "number", type: "number",
name: "gpsEnGpio", name: "gpsEnGpio",
label: "Enable Pin", label: t("Enable Pin"),
description: "GPS module enable pin override", description: t("GPS module enable pin override"),
}, },
], ],
}, },
{ {
label: "Intervals", label: t("Intervals"),
description: "How often to send position updates", description: t("How often to send position updates"),
fields: [ fields: [
{ {
type: "number", type: "number",
name: "positionBroadcastSecs", name: "positionBroadcastSecs",
label: "Broadcast Interval", label: t("Broadcast Interval"),
description: "How often your position is sent out over the mesh", description: t("How often your position is sent out over the mesh"),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },
@ -95,8 +100,8 @@ export const Position = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "gpsUpdateInterval", name: "gpsUpdateInterval",
label: "GPS Update Interval", label: t("GPS Update Interval"),
description: "How often a GPS fix should be acquired", description: t("How often a GPS fix should be acquired"),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },
@ -104,9 +109,9 @@ export const Position = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "broadcastSmartMinimumDistance", name: "broadcastSmartMinimumDistance",
label: "Smart Position Minimum Distance", label: t("Smart Position Minimum Distance"),
description: description:
"Minimum distance (in meters) that must be traveled before a position update is sent", t("Minimum distance (in meters) that must be traveled before a position update is sent"),
disabledBy: [ disabledBy: [
{ {
fieldName: "positionBroadcastSmartEnabled", fieldName: "positionBroadcastSmartEnabled",
@ -116,9 +121,9 @@ export const Position = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "broadcastSmartMinimumIntervalSecs", name: "broadcastSmartMinimumIntervalSecs",
label: "Smart Position Minimum Interval", label: t("Smart Position Minimum Interval"),
description: description:
"Minimum interval (in seconds) that must pass before a position update is sent", t("Minimum interval (in seconds) that must pass before a position update is sent"),
disabledBy: [ disabledBy: [
{ {
fieldName: "positionBroadcastSmartEnabled", fieldName: "positionBroadcastSmartEnabled",

59
src/components/PageComponents/Config/Power.tsx

@ -2,9 +2,11 @@ import type { PowerValidation } from "@app/validation/config/power.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
import { useTranslation } from "react-i18next";
export const Power = (): JSX.Element => { export const Power = (): JSX.Element => {
const { config, setWorkingConfig } = useDevice(); const { config, setWorkingConfig } = useDevice();
const { t } = useTranslation();
const onSubmit = (data: PowerValidation) => { const onSubmit = (data: PowerValidation) => {
setWorkingConfig( setWorkingConfig(
@ -13,7 +15,7 @@ export const Power = (): JSX.Element => {
case: "power", case: "power",
value: data, value: data,
}, },
}), })
); );
}; };
@ -23,22 +25,24 @@ export const Power = (): JSX.Element => {
defaultValues={config.power} defaultValues={config.power}
fieldGroups={[ fieldGroups={[
{ {
label: "Power Config", label: t("Power Config"),
description: "Settings for the power module", description: t("Settings for the power module"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "isPowerSaving", name: "isPowerSaving",
label: "Enable power saving mode", label: t("Enable power saving mode"),
description: description: t(
"Select if powered from a low-current source (i.e. solar), to minimize power consumption as much as possible.", "Select if powered from a low-current source (i.e. solar), to minimize power consumption as much as possible."
),
}, },
{ {
type: "number", type: "number",
name: "onBatteryShutdownAfterSecs", name: "onBatteryShutdownAfterSecs",
label: "Shutdown on battery delay", label: t("Shutdown on battery delay"),
description: description: t(
"Automatically shutdown node after this long when on battery, 0 for indefinite", "Automatically shutdown node after this long when on battery, 0 for indefinite"
),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },
@ -46,8 +50,8 @@ export const Power = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "adcMultiplierOverride", name: "adcMultiplierOverride",
label: "ADC Multiplier Override ratio", label: t("ADC Multiplier Override ratio"),
description: "Used for tweaking battery voltage reading", description: t("Used for tweaking battery voltage reading"),
properties: { properties: {
step: 0.0001, step: 0.0001,
}, },
@ -55,9 +59,10 @@ export const Power = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "waitBluetoothSecs", name: "waitBluetoothSecs",
label: "No Connection Bluetooth Disabled", label: t("No Connection Bluetooth Disabled"),
description: description: t(
"If the device does not receive a Bluetooth connection, the BLE radio will be disabled after this long", "If the device does not receive a Bluetooth connection, the BLE radio will be disabled after this long"
),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },
@ -65,21 +70,22 @@ export const Power = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "deviceBatteryInaAddress", name: "deviceBatteryInaAddress",
label: "INA219 Address", label: t("INA219 Address"),
description: "Address of the INA219 battery monitor", description: t("Address of the INA219 battery monitor"),
}, },
], ],
}, },
{ {
label: "Sleep Settings", label: t("Sleep Settings"),
description: "Sleep settings for the power module", description: t("Sleep settings for the power module"),
fields: [ fields: [
{ {
type: "number", type: "number",
name: "sdsSecs", name: "sdsSecs",
label: "Super Deep Sleep Duration", label: t("Super Deep Sleep Duration"),
description: description: t(
"How long the device will be in super deep sleep for", "How long the device will be in super deep sleep for"
),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },
@ -87,8 +93,8 @@ export const Power = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "lsSecs", name: "lsSecs",
label: "Light Sleep Duration", label: t("Light Sleep Duration"),
description: "How long the device will be in light sleep for", description: t("How long the device will be in light sleep for"),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },
@ -96,9 +102,10 @@ export const Power = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "minWakeSecs", name: "minWakeSecs",
label: "Minimum Wake Time", label: t("Minimum Wake Time"),
description: description: t(
"Minimum amount of time the device will stay awake for after receiving a packet", "Minimum amount of time the device will stay awake for after receiving a packet"
),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },

85
src/components/PageComponents/Config/Security.tsx

@ -7,27 +7,29 @@ import {
import type { SecurityValidation } from "@app/validation/config/security.tsx"; import type { SecurityValidation } from "@app/validation/config/security.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
import { useTranslation } from "react-i18next";
import { fromByteArray, toByteArray } from "base64-js"; import { fromByteArray, toByteArray } from "base64-js";
import { Eye, EyeOff } from "lucide-react"; import { Eye, EyeOff } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
export const Security = (): JSX.Element => { export const Security = (): JSX.Element => {
const { config, nodes, hardware, setWorkingConfig } = useDevice(); const { config, nodes, hardware, setWorkingConfig } = useDevice();
const { t } = useTranslation();
const [privateKey, setPrivateKey] = useState<string>( const [privateKey, setPrivateKey] = useState<string>(
fromByteArray(config.security?.privateKey ?? new Uint8Array(0)), fromByteArray(config.security?.privateKey ?? new Uint8Array(0))
); );
const [privateKeyVisible, setPrivateKeyVisible] = useState<boolean>(false); const [privateKeyVisible, setPrivateKeyVisible] = useState<boolean>(false);
const [privateKeyBitCount, setPrivateKeyBitCount] = useState<number>( const [privateKeyBitCount, setPrivateKeyBitCount] = useState<number>(
config.security?.privateKey.length ?? 32, config.security?.privateKey.length ?? 32
); );
const [privateKeyValidationText, setPrivateKeyValidationText] = const [privateKeyValidationText, setPrivateKeyValidationText] =
useState<string>(); useState<string>();
const [publicKey, setPublicKey] = useState<string>( const [publicKey, setPublicKey] = useState<string>(
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[0] ?? new Uint8Array(0)), fromByteArray(config.security?.adminKey[0] ?? new Uint8Array(0))
); );
const [adminKeyValidationText, setAdminKeyValidationText] = const [adminKeyValidationText, setAdminKeyValidationText] =
useState<string>(); useState<string>();
@ -47,26 +49,28 @@ export const Security = (): JSX.Element => {
publicKey: toByteArray(publicKey), publicKey: toByteArray(publicKey),
}, },
}, },
}), })
); );
}; };
const validateKey = ( const validateKey = (
input: string, input: string,
count: number, count: number,
setValidationText: ( setValidationText: (value: React.SetStateAction<string | undefined>) => void
value: React.SetStateAction<string | undefined>,
) => void,
) => { ) => {
try { try {
if (input.length % 4 !== 0 || toByteArray(input).length !== count) { if (input.length % 4 !== 0 || toByteArray(input).length !== count) {
setValidationText(`Please enter a valid ${count * 8} bit PSK.`); setValidationText(
t("Please enter a valid bit PSK.", { count: count * 8 })
);
} else { } else {
setValidationText(undefined); setValidationText(undefined);
} }
} catch (e) { } catch (e) {
console.error(e); console.error(e);
setValidationText(`Please enter a valid ${count * 8} bit PSK.`); setValidationText(
t("Please enter a valid bit PSK.", { count: count * 8 })
);
} }
}; };
@ -83,21 +87,21 @@ export const Security = (): JSX.Element => {
validateKey( validateKey(
fromByteArray(privateKey), fromByteArray(privateKey),
privateKeyBitCount, privateKeyBitCount,
setPrivateKeyValidationText, setPrivateKeyValidationText
); );
setDialogOpen(false); setDialogOpen(false);
}; };
const privateKeyInputChangeEvent = ( const privateKeyInputChangeEvent = (
e: React.ChangeEvent<HTMLInputElement>, e: React.ChangeEvent<HTMLInputElement>
) => { ) => {
const privateKeyB64String = e.target.value; const privateKeyB64String = e.target.value;
setPrivateKey(privateKeyB64String); setPrivateKey(privateKeyB64String);
validateKey( validateKey(
privateKeyB64String, privateKeyB64String,
privateKeyBitCount, privateKeyBitCount,
setPrivateKeyValidationText, setPrivateKeyValidationText
); );
const publicKey = getX25519PublicKey(toByteArray(privateKeyB64String)); const publicKey = getX25519PublicKey(toByteArray(privateKeyB64String));
@ -135,14 +139,16 @@ export const Security = (): JSX.Element => {
}} }}
fieldGroups={[ fieldGroups={[
{ {
label: "Security Settings", label: t("Security Settings"),
description: "Settings for the Security configuration", description: "Settings for the Security configuration",
fields: [ fields: [
{ {
type: "passwordGenerator", type: "passwordGenerator",
name: "privateKey", name: "privateKey",
label: "Private Key", label: t("Private Key"),
description: "Used to create a shared key with a remote device", description: t(
"Used to create a shared key with a remote device"
),
bits: [{ text: "256 bit", value: "32", key: "bit256" }], bits: [{ text: "256 bit", value: "32", key: "bit256" }],
validationText: privateKeyValidationText, validationText: privateKeyValidationText,
devicePSKBitCount: privateKeyBitCount, devicePSKBitCount: privateKeyBitCount,
@ -161,10 +167,11 @@ export const Security = (): JSX.Element => {
{ {
type: "text", type: "text",
name: "publicKey", name: "publicKey",
label: "Public Key", label: t("Public Key"),
disabled: true, disabled: true,
description: description: t(
"Sent out to other nodes on the mesh to allow them to compute a shared secret key", "Sent out to other nodes on the mesh to allow them to compute a shared secret key"
),
properties: { properties: {
value: publicKey, value: publicKey,
}, },
@ -172,29 +179,32 @@ export const Security = (): JSX.Element => {
], ],
}, },
{ {
label: "Admin Settings", label: t("Admin Settings"),
description: "Settings for Admin", description: t("Settings for Admin"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "adminChannelEnabled", name: "adminChannelEnabled",
label: "Allow Legacy Admin", label: t("Allow Legacy Admin"),
description: description: t(
"Allow incoming device control over the insecure legacy admin channel", "Allow incoming device control over the insecure legacy admin channel"
),
}, },
{ {
type: "toggle", type: "toggle",
name: "isManaged", name: "isManaged",
label: "Managed", label: t("Managed"),
description: description: t(
'If true, device configuration options are only able to be changed remotely by a Remote Admin node via admin messages. Do not enable this option unless a suitable Remote Admin node has been setup, and the public key stored in the field below.', 'If true, device is considered to be "managed" by a mesh administrator via admin messages'
),
}, },
{ {
type: "text", type: "text",
name: "adminKey", name: "adminKey",
label: "Admin Key", label: t("Admin Key"),
description: description: t(
"The public key authorized to send admin messages to this node", "The public key authorized to send admin messages to this node"
),
validationText: adminKeyValidationText, validationText: adminKeyValidationText,
inputChange: adminKeyInputChangeEvent, inputChange: adminKeyInputChangeEvent,
disabledBy: [ disabledBy: [
@ -207,21 +217,22 @@ export const Security = (): JSX.Element => {
], ],
}, },
{ {
label: "Logging Settings", label: t("Logging Settings"),
description: "Settings for Logging", description: t("Settings for Logging"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "debugLogApiEnabled", name: "debugLogApiEnabled",
label: "Enable Debug Log API", label: t("Enable Debug Log API"),
description: description: t(
"Output live debug logging over serial, view and export position-redacted device logs over Bluetooth", "Output live debug logging over serial, view and export position-redacted device logs over Bluetooth"
),
}, },
{ {
type: "toggle", type: "toggle",
name: "serialEnabled", name: "serialEnabled",
label: "Serial Output Enabled", label: t("Serial Output Enabled"),
description: "Serial Console over the Stream API", description: t("Serial Console over the Stream API"),
}, },
], ],
}, },

8
src/components/PageComponents/Connect/BLE.tsx

@ -7,11 +7,13 @@ import { subscribeAll } from "@core/subscriptions.ts";
import { randId } from "@core/utils/randId.ts"; import { randId } from "@core/utils/randId.ts";
import { BleConnection, Constants } from "@meshtastic/js"; import { BleConnection, Constants } from "@meshtastic/js";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
export const BLE = ({ closeDialog }: TabElementProps): JSX.Element => { export const BLE = ({ closeDialog }: TabElementProps): JSX.Element => {
const [bleDevices, setBleDevices] = useState<BluetoothDevice[]>([]); const [bleDevices, setBleDevices] = useState<BluetoothDevice[]>([]);
const { addDevice } = useDeviceStore(); const { addDevice } = useDeviceStore();
const { setSelectedDevice } = useAppStore(); const { setSelectedDevice } = useAppStore();
const { t } = useTranslation();
const updateBleDeviceList = useCallback(async (): Promise<void> => { const updateBleDeviceList = useCallback(async (): Promise<void> => {
setBleDevices(await navigator.bluetooth.getDevices()); setBleDevices(await navigator.bluetooth.getDevices());
@ -49,7 +51,9 @@ export const BLE = ({ closeDialog }: TabElementProps): JSX.Element => {
</Button> </Button>
))} ))}
{bleDevices.length === 0 && ( {bleDevices.length === 0 && (
<Mono className="m-auto select-none">No devices paired yet.</Mono> <Mono className="m-auto select-none">
{t("No devices paired yet.")}
</Mono>
)} )}
</div> </div>
<Button <Button
@ -66,7 +70,7 @@ export const BLE = ({ closeDialog }: TabElementProps): JSX.Element => {
}); });
}} }}
> >
<span>New device</span> <span>{t("New device")}</span>
</Button> </Button>
</div> </div>
); );

12
src/components/PageComponents/Connect/HTTP.tsx

@ -10,17 +10,19 @@ import { randId } from "@core/utils/randId.ts";
import { HttpConnection } from "@meshtastic/js"; import { HttpConnection } from "@meshtastic/js";
import { useState } from "react"; import { useState } from "react";
import { Controller, useForm, useWatch } from "react-hook-form"; import { Controller, useForm, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
export const HTTP = ({ closeDialog }: TabElementProps): JSX.Element => { export const HTTP = ({ closeDialog }: TabElementProps): JSX.Element => {
const { addDevice } = useDeviceStore(); const { addDevice } = useDeviceStore();
const { setSelectedDevice } = useAppStore(); const { setSelectedDevice } = useAppStore();
const { t } = useTranslation();
const { register, handleSubmit, control } = useForm<{ const { register, handleSubmit, control } = useForm<{
ip: string; ip: string;
tls: boolean; tls: boolean;
}>({ }>({
defaultValues: { defaultValues: {
ip: ["client.meshtastic.org", "localhost"].includes( ip: ["client.meshtastic.org", "localhost"].includes(
window.location.hostname, window.location.hostname
) )
? "meshtastic.local" ? "meshtastic.local"
: window.location.hostname, : window.location.hostname,
@ -58,7 +60,7 @@ export const HTTP = ({ closeDialog }: TabElementProps): JSX.Element => {
return ( return (
<form className="flex w-full flex-col gap-2 p-4" onSubmit={onSubmit}> <form className="flex w-full flex-col gap-2 p-4" onSubmit={onSubmit}>
<div className="flex h-48 flex-col gap-2"> <div className="flex h-48 flex-col gap-2">
<Label>IP Address/Hostname</Label> <Label>{t("IP Address/Hostname")}</Label>
<Input <Input
// label="IP Address/Hostname" // label="IP Address/Hostname"
prefix={tlsEnabled ? "https://" : "http://"} prefix={tlsEnabled ? "https://" : "http://"}
@ -71,7 +73,7 @@ export const HTTP = ({ closeDialog }: TabElementProps): JSX.Element => {
control={control} control={control}
render={({ field: { value, ...rest } }) => ( render={({ field: { value, ...rest } }) => (
<> <>
<Label>Use TLS</Label> <Label>{t("Use TLS")}</Label>
<Switch <Switch
// label="Use TLS" // label="Use TLS"
// description="Description" // description="Description"
@ -86,7 +88,9 @@ export const HTTP = ({ closeDialog }: TabElementProps): JSX.Element => {
/> />
</div> </div>
<Button type="submit" disabled={connectionInProgress}> <Button type="submit" disabled={connectionInProgress}>
<span>{connectionInProgress ? "Connecting..." : "Connect"}</span> <span>
{connectionInProgress ? t("Connecting...") : t("Connected")}
</span>
</Button> </Button>
</form> </form>
); );

8
src/components/PageComponents/Connect/Serial.tsx

@ -7,11 +7,13 @@ import { subscribeAll } from "@core/subscriptions.ts";
import { randId } from "@core/utils/randId.ts"; import { randId } from "@core/utils/randId.ts";
import { SerialConnection } from "@meshtastic/js"; import { SerialConnection } from "@meshtastic/js";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
export const Serial = ({ closeDialog }: TabElementProps): JSX.Element => { export const Serial = ({ closeDialog }: TabElementProps): JSX.Element => {
const [serialPorts, setSerialPorts] = useState<SerialPort[]>([]); const [serialPorts, setSerialPorts] = useState<SerialPort[]>([]);
const { addDevice } = useDeviceStore(); const { addDevice } = useDeviceStore();
const { setSelectedDevice } = useAppStore(); const { setSelectedDevice } = useAppStore();
const { t } = useTranslation();
const updateSerialPortList = useCallback(async () => { const updateSerialPortList = useCallback(async () => {
setSerialPorts(await navigator.serial.getPorts()); setSerialPorts(await navigator.serial.getPorts());
@ -38,7 +40,7 @@ export const Serial = ({ closeDialog }: TabElementProps): JSX.Element => {
baudRate: undefined, baudRate: undefined,
concurrentLogOutput: true, concurrentLogOutput: true,
}) })
.catch((e: Error) => console.log(`Unable to Connect: ${e.message}`)); .catch((e: Error) => console.log(`${"Unable to Connect:"} ${e.message}`));
device.addConnection(connection); device.addConnection(connection);
subscribeAll(device, connection); subscribeAll(device, connection);
@ -65,7 +67,7 @@ export const Serial = ({ closeDialog }: TabElementProps): JSX.Element => {
); );
})} })}
{serialPorts.length === 0 && ( {serialPorts.length === 0 && (
<Mono className="m-auto select-none">No devices paired yet.</Mono> <Mono className="m-auto select-none">{t("No devices paired yet.")}</Mono>
)} )}
</div> </div>
<Button <Button
@ -75,7 +77,7 @@ export const Serial = ({ closeDialog }: TabElementProps): JSX.Element => {
}); });
}} }}
> >
<span>New device</span> <span>{t("New device")}</span>
</Button> </Button>
</div> </div>
); );

10
src/components/PageComponents/Messages/ChannelChat.tsx

@ -7,6 +7,7 @@ import { Message } from "@components/PageComponents/Messages/Message.tsx";
import { MessageInput } from "@components/PageComponents/Messages/MessageInput.tsx"; import { MessageInput } from "@components/PageComponents/Messages/MessageInput.tsx";
import { TraceRoute } from "@components/PageComponents/Messages/TraceRoute.tsx"; import { TraceRoute } from "@components/PageComponents/Messages/TraceRoute.tsx";
import type { Protobuf, Types } from "@meshtastic/js"; import type { Protobuf, Types } from "@meshtastic/js";
import { useTranslation } from "react-i18next";
import { InboxIcon } from "lucide-react"; import { InboxIcon } from "lucide-react";
export interface ChannelChatProps { export interface ChannelChatProps {
@ -23,6 +24,7 @@ export const ChannelChat = ({
traceroutes, traceroutes,
}: ChannelChatProps): JSX.Element => { }: ChannelChatProps): JSX.Element => {
const { nodes } = useDevice(); const { nodes } = useDevice();
const { t } = useTranslation();
return ( return (
<div className="flex flex-grow flex-col"> <div className="flex flex-grow flex-col">
@ -44,12 +46,14 @@ export const ChannelChat = ({
) : ( ) : (
<div className="m-auto"> <div className="m-auto">
<InboxIcon className="m-auto" /> <InboxIcon className="m-auto" />
<Subtle>No Messages</Subtle> <Subtle>{t("No Messages")}</Subtle>
</div> </div>
)} )}
</div> </div>
<div <div
className={`flex flex-grow flex-col border-slate-400 border-l ${traceroutes === undefined ? "hidden" : ""}`} className={`flex flex-grow flex-col border-slate-400 border-l ${
traceroutes === undefined ? "hidden" : ""
}`}
> >
{to === "broadcast" ? null : traceroutes ? ( {to === "broadcast" ? null : traceroutes ? (
traceroutes.map((traceroute, index) => ( traceroutes.map((traceroute, index) => (
@ -63,7 +67,7 @@ export const ChannelChat = ({
) : ( ) : (
<div className="m-auto"> <div className="m-auto">
<InboxIcon className="m-auto" /> <InboxIcon className="m-auto" />
<Subtle>No Traceroutes</Subtle> <Subtle>{t("No Traceroutes")}</Subtle>
</div> </div>
)} )}
</div> </div>

12
src/components/PageComponents/Messages/MessageInput.tsx

@ -3,6 +3,7 @@ import { Input } from "@components/UI/Input.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import type { Types } from "@meshtastic/js"; import type { Types } from "@meshtastic/js";
import { SendIcon } from "lucide-react"; import { SendIcon } from "lucide-react";
import { useTranslation } from "react-i18next";
export interface MessageInputProps { export interface MessageInputProps {
to: Types.Destination; to: Types.Destination;
@ -20,6 +21,7 @@ export const MessageInput = ({
setMessageDraft, setMessageDraft,
hardware, hardware,
} = useDevice(); } = useDevice();
const { t } = useTranslation();
const myNodeNum = hardware.myNodeNum; const myNodeNum = hardware.myNodeNum;
@ -33,8 +35,8 @@ export const MessageInput = ({
to as number, to as number,
myNodeNum, myNodeNum,
id, id,
"ack", "ack"
), )
) )
.catch((e: Types.PacketError) => .catch((e: Types.PacketError) =>
setMessageState( setMessageState(
@ -43,8 +45,8 @@ export const MessageInput = ({
to as number, to as number,
myNodeNum, myNodeNum,
e.id, e.id,
e.error, e.error
), )
); );
}; };
@ -63,7 +65,7 @@ export const MessageInput = ({
<Input <Input
autoFocus={true} autoFocus={true}
minLength={1} minLength={1}
placeholder="Enter Message" placeholder={t("Enter Message")}
value={messageDraft} value={messageDraft}
onChange={(e) => setMessageDraft(e.target.value)} onChange={(e) => setMessageDraft(e.target.value)}
/> />

7
src/components/PageComponents/Messages/TraceRoute.tsx

@ -1,6 +1,7 @@
import { useDevice } from "@app/core/stores/deviceStore.ts"; import { useDevice } from "@app/core/stores/deviceStore.ts";
import type { Protobuf } from "@meshtastic/js"; import type { Protobuf } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils"; import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
import { useTranslation } from "react-i18next";
export interface TraceRouteProps { export interface TraceRouteProps {
from?: Protobuf.Mesh.NodeInfo; from?: Protobuf.Mesh.NodeInfo;
@ -14,6 +15,7 @@ export const TraceRoute = ({
route, route,
}: TraceRouteProps): JSX.Element => { }: TraceRouteProps): JSX.Element => {
const { nodes } = useDevice(); const { nodes } = useDevice();
const { t } = useTranslation("translation");
return route.length === 0 ? ( return route.length === 0 ? (
<div className="ml-5 flex"> <div className="ml-5 flex">
@ -27,7 +29,10 @@ export const TraceRoute = ({
{to?.user?.longName} {to?.user?.longName}
{route.map((hop) => { {route.map((hop) => {
const node = nodes.get(hop); const node = nodes.get(hop);
return `${node?.user?.longName ?? (node?.num ? numberToHexUnpadded(node.num) : "Unknown")}`; return `${
node?.user?.longName ??
(node?.num ? numberToHexUnpadded(node.num) : t("Unknown"))
}`;
})} })}
{from?.user?.longName} {from?.user?.longName}
</span> </span>

26
src/components/PageComponents/ModuleConfig/AmbientLighting.tsx

@ -1,10 +1,12 @@
import { useDevice } from "@app/core/stores/deviceStore.ts"; import { useDevice } from "@app/core/stores/deviceStore.ts";
import type { AmbientLightingValidation } from "@app/validation/moduleConfig/ambientLighting.tsx"; import type { AmbientLightingValidation } from "@app/validation/moduleConfig/ambientLighting.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useTranslation } from "react-i18next";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
export const AmbientLighting = (): JSX.Element => { export const AmbientLighting = (): JSX.Element => {
const { moduleConfig, setWorkingModuleConfig } = useDevice(); const { moduleConfig, setWorkingModuleConfig } = useDevice();
const { t } = useTranslation("translation");
const onSubmit = (data: AmbientLightingValidation) => { const onSubmit = (data: AmbientLightingValidation) => {
setWorkingModuleConfig( setWorkingModuleConfig(
@ -13,7 +15,7 @@ export const AmbientLighting = (): JSX.Element => {
case: "ambientLighting", case: "ambientLighting",
value: data, value: data,
}, },
}), })
); );
}; };
@ -29,32 +31,34 @@ export const AmbientLighting = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "ledState", name: "ledState",
label: "LED State", label: t("LED State"),
description: "Sets LED to on or off", description: t("Sets LED to on or off"),
}, },
{ {
type: "number", type: "number",
name: "current", name: "current",
label: "Current", label: t("Current"),
description: "Sets the current for the LED output. Default is 10", description: t(
"Sets the current for the LED output. Default is 10"
),
}, },
{ {
type: "number", type: "number",
name: "red", name: "red",
label: "Red", label: t("Red"),
description: "Sets the red LED level. Values are 0-255", description: t("Sets the red LED level. Values are 0-255"),
}, },
{ {
type: "number", type: "number",
name: "green", name: "green",
label: "Green", label: t("Green"),
description: "Sets the green LED level. Values are 0-255", description: t("Sets the green LED level. Values are 0-255"),
}, },
{ {
type: "number", type: "number",
name: "blue", name: "blue",
label: "Blue", label: t("Blue"),
description: "Sets the blue LED level. Values are 0-255", description: t("Sets the blue LED level. Values are 0-255"),
}, },
], ],
}, },

36
src/components/PageComponents/ModuleConfig/Audio.tsx

@ -1,10 +1,12 @@
import type { AudioValidation } from "@app/validation/moduleConfig/audio.tsx"; import type { AudioValidation } from "@app/validation/moduleConfig/audio.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useTranslation } from "react-i18next";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
export const Audio = (): JSX.Element => { export const Audio = (): JSX.Element => {
const { moduleConfig, setWorkingModuleConfig } = useDevice(); const { moduleConfig, setWorkingModuleConfig } = useDevice();
const { t } = useTranslation();
const onSubmit = (data: AudioValidation) => { const onSubmit = (data: AudioValidation) => {
setWorkingModuleConfig( setWorkingModuleConfig(
@ -13,7 +15,7 @@ export const Audio = (): JSX.Element => {
case: "audio", case: "audio",
value: data, value: data,
}, },
}), })
); );
}; };
@ -23,26 +25,26 @@ export const Audio = (): JSX.Element => {
defaultValues={moduleConfig.audio} defaultValues={moduleConfig.audio}
fieldGroups={[ fieldGroups={[
{ {
label: "Audio Settings", label: t("Audio Settings"),
description: "Settings for the Audio module", description: t("Settings for the Audio module"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "codec2Enabled", name: "codec2Enabled",
label: "Codec 2 Enabled", label: t("Codec 2 Enabled"),
description: "Enable Codec 2 audio encoding", description: t("Enable Codec 2 audio encoding"),
}, },
{ {
type: "number", type: "number",
name: "pttPin", name: "pttPin",
label: "PTT Pin", label: t("PTT Pin"),
description: "GPIO pin to use for PTT", description: t("GPIO pin to use for PTT"),
}, },
{ {
type: "select", type: "select",
name: "bitrate", name: "bitrate",
label: "Bitrate", label: t("Bitrate"),
description: "Bitrate to use for audio encoding", description: t("Bitrate to use for audio encoding"),
properties: { properties: {
enumValue: enumValue:
Protobuf.ModuleConfig.ModuleConfig_AudioConfig_Audio_Baud, Protobuf.ModuleConfig.ModuleConfig_AudioConfig_Audio_Baud,
@ -51,26 +53,26 @@ export const Audio = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "i2sWs", name: "i2sWs",
label: "i2S WS", label: t("i2S WS"),
description: "GPIO pin to use for i2S WS", description: t("GPIO pin to use for i2S WS"),
}, },
{ {
type: "number", type: "number",
name: "i2sSd", name: "i2sSd",
label: "i2S SD", label: t("i2S SD"),
description: "GPIO pin to use for i2S SD", description: t("GPIO pin to use for i2S SD"),
}, },
{ {
type: "number", type: "number",
name: "i2sDin", name: "i2sDin",
label: "i2S DIN", label: t("i2S DIN"),
description: "GPIO pin to use for i2S DIN", description: t("GPIO pin to use for i2S DIN"),
}, },
{ {
type: "number", type: "number",
name: "i2sSck", name: "i2sSck",
label: "i2S SCK", label: t("i2S SCK"),
description: "GPIO pin to use for i2S SCK", description: t("GPIO pin to use for i2S SCK"),
}, },
], ],
}, },

50
src/components/PageComponents/ModuleConfig/CannedMessage.tsx

@ -1,10 +1,12 @@
import type { CannedMessageValidation } from "@app/validation/moduleConfig/cannedMessage.tsx"; import type { CannedMessageValidation } from "@app/validation/moduleConfig/cannedMessage.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useTranslation } from "react-i18next";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
export const CannedMessage = (): JSX.Element => { export const CannedMessage = (): JSX.Element => {
const { moduleConfig, setWorkingModuleConfig } = useDevice(); const { moduleConfig, setWorkingModuleConfig } = useDevice();
const { t } = useTranslation();
const onSubmit = (data: CannedMessageValidation) => { const onSubmit = (data: CannedMessageValidation) => {
setWorkingModuleConfig( setWorkingModuleConfig(
@ -13,7 +15,7 @@ export const CannedMessage = (): JSX.Element => {
case: "cannedMessage", case: "cannedMessage",
value: data, value: data,
}, },
}), })
); );
}; };
@ -23,44 +25,44 @@ export const CannedMessage = (): JSX.Element => {
defaultValues={moduleConfig.cannedMessage} defaultValues={moduleConfig.cannedMessage}
fieldGroups={[ fieldGroups={[
{ {
label: "Canned Message Settings", label: t("Canned Message Settings"),
description: "Settings for the Canned Message module", description: t("Settings for the Canned Message module"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "enabled", name: "enabled",
label: "Module Enabled", label: t("Module Enabled"),
description: "Enable Canned Message", description: t("Enable Canned Message"),
}, },
{ {
type: "toggle", type: "toggle",
name: "rotary1Enabled", name: "rotary1Enabled",
label: "Rotary Encoder #1 Enabled", label: t("Rotary Encoder #1 Enabled"),
description: "Enable the rotary encoder", description: t("Enable the rotary encoder"),
}, },
{ {
type: "number", type: "number",
name: "inputbrokerPinA", name: "inputbrokerPinA",
label: "Encoder Pin A", label: t("Encoder Pin A"),
description: "GPIO Pin Value (1-39) For encoder port A", description: t("GPIO Pin Value (1-39) For encoder port A"),
}, },
{ {
type: "number", type: "number",
name: "inputbrokerPinB", name: "inputbrokerPinB",
label: "Encoder Pin B", label: t("Encoder Pin B"),
description: "GPIO Pin Value (1-39) For encoder port B", description: t("GPIO Pin Value (1-39) For encoder port B"),
}, },
{ {
type: "number", type: "number",
name: "inputbrokerPinPress", name: "inputbrokerPinPress",
label: "Encoder Pin Press", label: t("Encoder Pin Press"),
description: "GPIO Pin Value (1-39) For encoder Press", description: t("GPIO Pin Value (1-39) For encoder Press"),
}, },
{ {
type: "select", type: "select",
name: "inputbrokerEventCw", name: "inputbrokerEventCw",
label: "Clockwise event", label: t("Clockwise event"),
description: "Select input event.", description: t(t("Select input event.")),
properties: { properties: {
enumValue: enumValue:
Protobuf.ModuleConfig Protobuf.ModuleConfig
@ -70,8 +72,8 @@ export const CannedMessage = (): JSX.Element => {
{ {
type: "select", type: "select",
name: "inputbrokerEventCcw", name: "inputbrokerEventCcw",
label: "Counter Clockwise event", label: t("Counter Clockwise event"),
description: "Select input event.", description: t("Select input event."),
properties: { properties: {
enumValue: enumValue:
Protobuf.ModuleConfig Protobuf.ModuleConfig
@ -81,8 +83,8 @@ export const CannedMessage = (): JSX.Element => {
{ {
type: "select", type: "select",
name: "inputbrokerEventPress", name: "inputbrokerEventPress",
label: "Press event", label: t("Press event"),
description: "Select input event", description: t("Select input event."),
properties: { properties: {
enumValue: enumValue:
Protobuf.ModuleConfig Protobuf.ModuleConfig
@ -92,21 +94,21 @@ export const CannedMessage = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "updown1Enabled", name: "updown1Enabled",
label: "Up Down enabled", label: t("Up Down enabled"),
description: "Enable the up / down encoder", description: t("Enable the up / down encoder"),
}, },
{ {
type: "text", type: "text",
name: "allowInputSource", name: "allowInputSource",
label: "Allow Input Source", label: t("Allow Input Source"),
description: description:
"Select from: '_any', 'rotEnc1', 'upDownEnc1', 'cardkb'", "Select from: '_any', 'rotEnc1', 'upDownEnc1', 'cardkb'",
}, },
{ {
type: "toggle", type: "toggle",
name: "sendBell", name: "sendBell",
label: "Send Bell", label: t("Send Bell"),
description: "Sends a bell character with each message", description: t("Sends a bell character with each message"),
}, },
], ],
}, },

54
src/components/PageComponents/ModuleConfig/DetectionSensor.tsx

@ -1,10 +1,12 @@
import { useDevice } from "@app/core/stores/deviceStore.ts"; import { useDevice } from "@app/core/stores/deviceStore.ts";
import type { DetectionSensorValidation } from "@app/validation/moduleConfig/detectionSensor.tsx"; import type { DetectionSensorValidation } from "@app/validation/moduleConfig/detectionSensor.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useTranslation } from "react-i18next";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
export const DetectionSensor = (): JSX.Element => { export const DetectionSensor = (): JSX.Element => {
const { moduleConfig, setWorkingModuleConfig } = useDevice(); const { moduleConfig, setWorkingModuleConfig } = useDevice();
const { t } = useTranslation("translation");
const onSubmit = (data: DetectionSensorValidation) => { const onSubmit = (data: DetectionSensorValidation) => {
setWorkingModuleConfig( setWorkingModuleConfig(
@ -13,7 +15,7 @@ export const DetectionSensor = (): JSX.Element => {
case: "detectionSensor", case: "detectionSensor",
value: data, value: data,
}, },
}), })
); );
}; };
@ -23,21 +25,22 @@ export const DetectionSensor = (): JSX.Element => {
defaultValues={moduleConfig.detectionSensor} defaultValues={moduleConfig.detectionSensor}
fieldGroups={[ fieldGroups={[
{ {
label: "Detection Sensor Settings", label: t("Detection Sensor Settings"),
description: "Settings for the Detection Sensor module", description: t("Settings for the Detection Sensor module"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "enabled", name: "enabled",
label: "Enabled", label: t("Enabled"),
description: "Enable or disable Detection Sensor Module", description: t("Enable or disable Detection Sensor Module"),
}, },
{ {
type: "number", type: "number",
name: "minimumBroadcastSecs", name: "minimumBroadcastSecs",
label: "Minimum Broadcast Seconds", label: t("Minimum Broadcast Seconds"),
description: description: t(
"The interval in seconds of how often we can send a message to the mesh when a state change is detected", "The interval in seconds of how often we can send a message to the mesh when a state change is detected"
),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },
@ -50,9 +53,10 @@ export const DetectionSensor = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "stateBroadcastSecs", name: "stateBroadcastSecs",
label: "State Broadcast Seconds", label: t("State Broadcast Seconds"),
description: description: t(
"The interval in seconds of how often we should send a message to the mesh with the current state regardless of changes", "The interval in seconds of how often we should send a message to the mesh with the current state regardless of changes"
),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -62,8 +66,8 @@ export const DetectionSensor = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "sendBell", name: "sendBell",
label: "Send Bell", label: t("Send Bell"),
description: "Send ASCII bell with alert message", description: t("Send ASCII bell with alert message"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -73,9 +77,10 @@ export const DetectionSensor = (): JSX.Element => {
{ {
type: "text", type: "text",
name: "name", name: "name",
label: "Friendly Name", label: t("Friendly Name"),
description: description: t(
"Used to format the message sent to mesh, max 20 Characters", "Used to format the message sent to mesh, max 20 Characters"
),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -85,8 +90,8 @@ export const DetectionSensor = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "monitorPin", name: "monitorPin",
label: "Monitor Pin", label: t("Monitor Pin"),
description: "The GPIO pin to monitor for state changes", description: t("The GPIO pin to monitor for state changes"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -96,9 +101,10 @@ export const DetectionSensor = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "detectionTriggeredHigh", name: "detectionTriggeredHigh",
label: "Detection Triggered High", label: t("Detection Triggered High"),
description: description: t(
"Whether or not the GPIO pin state detection is triggered on HIGH (1), otherwise LOW (0)", "Whether or not the GPIO pin state detection is triggered on HIGH (1), otherwise LOW (0)"
),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -108,8 +114,10 @@ export const DetectionSensor = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "usePullup", name: "usePullup",
label: "Use Pullup", label: t("Use Pullup"),
description: "Whether or not use INPUT_PULLUP mode for GPIO pin", description: t(
"Whether or not use INPUT_PULLUP mode for GPIO pin"
),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",

69
src/components/PageComponents/ModuleConfig/ExternalNotification.tsx

@ -1,10 +1,12 @@
import type { ExternalNotificationValidation } from "@app/validation/moduleConfig/externalNotification.tsx"; import type { ExternalNotificationValidation } from "@app/validation/moduleConfig/externalNotification.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { useTranslation } from "react-i18next";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
export const ExternalNotification = (): JSX.Element => { export const ExternalNotification = (): JSX.Element => {
const { moduleConfig, setWorkingModuleConfig } = useDevice(); const { moduleConfig, setWorkingModuleConfig } = useDevice();
const { t } = useTranslation();
const onSubmit = (data: ExternalNotificationValidation) => { const onSubmit = (data: ExternalNotificationValidation) => {
setWorkingModuleConfig( setWorkingModuleConfig(
@ -13,7 +15,7 @@ export const ExternalNotification = (): JSX.Element => {
case: "externalNotification", case: "externalNotification",
value: data, value: data,
}, },
}), })
); );
}; };
@ -23,20 +25,20 @@ export const ExternalNotification = (): JSX.Element => {
defaultValues={moduleConfig.externalNotification} defaultValues={moduleConfig.externalNotification}
fieldGroups={[ fieldGroups={[
{ {
label: "External Notification Settings", label: t("External Notification Settings"),
description: "Configure the external notification module", description: "Configure the external notification module",
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "enabled", name: "enabled",
label: "Module Enabled", label: t("Module Enabled"),
description: "Enable External Notification", description: t("Enable External Notification"),
}, },
{ {
type: "number", type: "number",
name: "outputMs", name: "outputMs",
label: "Output MS", label: t("Output MS"),
description: "Output MS", description: t("Output MS"),
disabledBy: [ disabledBy: [
{ {
@ -50,8 +52,8 @@ export const ExternalNotification = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "output", name: "output",
label: "Output", label: t("Output"),
description: "Output", description: t("Output"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -61,8 +63,8 @@ export const ExternalNotification = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "outputVibra", name: "outputVibra",
label: "Output Vibrate", label: t("Output Vibrate"),
description: "Output Vibrate", description: t("Output Vibrate"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -72,8 +74,8 @@ export const ExternalNotification = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "outputBuzzer", name: "outputBuzzer",
label: "Output Buzzer", label: t("Output Buzzer"),
description: "Output Buzzer", description: t("Output Buzzer"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -83,8 +85,8 @@ export const ExternalNotification = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "active", name: "active",
label: "Active", label: t("Active"),
description: "Active", description: t("Active"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -94,8 +96,8 @@ export const ExternalNotification = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "alertMessage", name: "alertMessage",
label: "Alert Message", label: t("Alert Message"),
description: "Alert Message", description: t("Alert Message"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -105,8 +107,8 @@ export const ExternalNotification = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "alertMessageVibra", name: "alertMessageVibra",
label: "Alert Message Vibrate", label: t("Alert Message Vibrate"),
description: "Alert Message Vibrate", description: t("Alert Message Vibrate"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -116,8 +118,8 @@ export const ExternalNotification = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "alertMessageBuzzer", name: "alertMessageBuzzer",
label: "Alert Message Buzzer", label: t("Alert Message Buzzer"),
description: "Alert Message Buzzer", description: t("Alert Message Buzzer"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -127,9 +129,10 @@ export const ExternalNotification = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "alertBell", name: "alertBell",
label: "Alert Bell", label: t("Alert Bell"),
description: description: t(
"Should an alert be triggered when receiving an incoming bell?", "Should an alert be triggered when receiving an incoming bell?"
),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -139,8 +142,8 @@ export const ExternalNotification = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "alertBellVibra", name: "alertBellVibra",
label: "Alert Bell Vibrate", label: t("Alert Bell Vibrate"),
description: "Alert Bell Vibrate", description: t("Alert Bell Vibrate"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -150,8 +153,8 @@ export const ExternalNotification = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "alertBellBuzzer", name: "alertBellBuzzer",
label: "Alert Bell Buzzer", label: t("Alert Bell Buzzer"),
description: "Alert Bell Buzzer", description: t("Alert Bell Buzzer"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -161,8 +164,8 @@ export const ExternalNotification = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "usePwm", name: "usePwm",
label: "Use PWM", label: t("Use PWM"),
description: "Use PWM", description: t("Use PWM"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -172,8 +175,8 @@ export const ExternalNotification = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "nagTimeout", name: "nagTimeout",
label: "Nag Timeout", label: t("Nag Timeout"),
description: "Nag Timeout", description: t("Nag Timeout"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -183,8 +186,8 @@ export const ExternalNotification = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "useI2sAsBuzzer", name: "useI2sAsBuzzer",
label: "Use I²S Pin as Buzzer", label: t("Use I²S Pin as Buzzer"),
description: "Designate I²S Pin as Buzzer Output", description: t("Designate I²S Pin as Buzzer Output"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",

105
src/components/PageComponents/ModuleConfig/MQTT.tsx

@ -1,10 +1,12 @@
import { useDevice } from "@app/core/stores/deviceStore.ts"; import { useDevice } from "@app/core/stores/deviceStore.ts";
import type { MqttValidation } from "@app/validation/moduleConfig/mqtt.tsx"; import type { MqttValidation } from "@app/validation/moduleConfig/mqtt.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useTranslation } from "react-i18next";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
export const MQTT = (): JSX.Element => { export const MQTT = (): JSX.Element => {
const { config, moduleConfig, setWorkingModuleConfig } = useDevice(); const { config, moduleConfig, setWorkingModuleConfig } = useDevice();
const { t } = useTranslation();
const onSubmit = (data: MqttValidation) => { const onSubmit = (data: MqttValidation) => {
setWorkingModuleConfig( setWorkingModuleConfig(
@ -15,11 +17,11 @@ export const MQTT = (): JSX.Element => {
...data, ...data,
mapReportSettings: mapReportSettings:
new Protobuf.ModuleConfig.ModuleConfig_MapReportSettings( new Protobuf.ModuleConfig.ModuleConfig_MapReportSettings(
data.mapReportSettings, data.mapReportSettings
), ),
}, },
}, },
}), })
); );
}; };
@ -29,21 +31,22 @@ export const MQTT = (): JSX.Element => {
defaultValues={moduleConfig.mqtt} defaultValues={moduleConfig.mqtt}
fieldGroups={[ fieldGroups={[
{ {
label: "MQTT Settings", label: t("MQTT Settings"),
description: "Settings for the MQTT module", description: t("Settings for the MQTT module"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "enabled", name: "enabled",
label: "Enabled", label: t("Enabled"),
description: "Enable or disable MQTT", description: t("Enable or disable MQTT"),
}, },
{ {
type: "text", type: "text",
name: "address", name: "address",
label: "MQTT Server Address", label: t("MQTT Server Address"),
description: description: t(
"MQTT server address to use for default/custom servers", "MQTT server address to use for default/custom servers"
),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -53,8 +56,8 @@ export const MQTT = (): JSX.Element => {
{ {
type: "text", type: "text",
name: "username", name: "username",
label: "MQTT Username", label: t("MQTT Username"),
description: "MQTT username to use for default/custom servers", description: t("MQTT username to use for default/custom servers"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -64,8 +67,8 @@ export const MQTT = (): JSX.Element => {
{ {
type: "password", type: "password",
name: "password", name: "password",
label: "MQTT Password", label: t("MQTT Password"),
description: "MQTT password to use for default/custom servers", description: t("MQTT password to use for default/custom servers"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -75,9 +78,10 @@ export const MQTT = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "encryptionEnabled", name: "encryptionEnabled",
label: "Encryption Enabled", label: t("Encryption Enabled"),
description: description: t(
"Enable or disable MQTT encryption. Note: All messages are sent to the MQTT broker unencrypted if this option is not enabled, even when your uplink channels have encryption keys set. This includes position data.", "Enable or disable MQTT encryption. Note: All messages are sent to the MQTT broker unencrypted if this option is not enabled, even when your uplink channels have encryption keys set. This includes position data."
),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -87,8 +91,8 @@ export const MQTT = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "jsonEnabled", name: "jsonEnabled",
label: "JSON Enabled", label: t("JSON Enabled"),
description: "Whether to send/consume JSON packets on MQTT", description: t("Whether to send/consume JSON packets on MQTT"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -98,8 +102,8 @@ export const MQTT = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "tlsEnabled", name: "tlsEnabled",
label: "TLS Enabled", label: t("TLS Enabled"),
description: "Enable or disable TLS", description: t("Enable or disable TLS"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -109,8 +113,10 @@ export const MQTT = (): JSX.Element => {
{ {
type: "text", type: "text",
name: "root", name: "root",
label: "Root topic", label: t("Root topic"),
description: "MQTT root topic to use for default/custom servers", description: t(
"MQTT root topic to use for default/custom servers"
),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -120,9 +126,10 @@ export const MQTT = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "proxyToClientEnabled", name: "proxyToClientEnabled",
label: "Proxy to Client Enabled", label: t("Proxy to Client Enabled"),
description: description: t(
"Use the client's internet connection for MQTT (feature only active in mobile apps)", "Use the client's internet connection for MQTT (feature only active in mobile apps)"
),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -132,8 +139,8 @@ export const MQTT = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "mapReportingEnabled", name: "mapReportingEnabled",
label: "Map Reporting Enabled", label: t("Map Reporting Enabled"),
description: "Enable or disable map reporting", description: t("Enable or disable map reporting"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -143,8 +150,8 @@ export const MQTT = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "mapReportSettings.publishIntervalSecs", name: "mapReportSettings.publishIntervalSecs",
label: "Map Report Publish Interval (s)", label: t("Map Report Publish Interval (s)"),
description: "Interval in seconds to publish map reports", description: t("Interval in seconds to publish map reports"),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },
@ -167,28 +174,28 @@ export const MQTT = (): JSX.Element => {
enumValue: enumValue:
config.display?.units === 0 config.display?.units === 0
? { ? {
"Within 23 km": 10, [t("Within km", { value: 23 })]: 10,
"Within 12 km": 11, [t("Within km", { value: 12 })]: 11,
"Within 5.8 km": 12, [t("Within km", { value: "5.8" })]: 12,
"Within 2.9 km": 13, [t("Within km", { value: "2.9" })]: 13,
"Within 1.5 km": 14, [t("Within km", { value: "1.5" })]: 14,
"Within 700 m": 15, [t("Within m", { value: "700" })]: 15,
"Within 350 m": 16, [t("Within m", { value: "350" })]: 16,
"Within 200 m": 17, [t("Within m", { value: "200" })]: 17,
"Within 90 m": 18, [t("Within m", { value: "90" })]: 18,
"Within 50 m": 19, [t("Within m", { value: "50" })]: 19,
} }
: { : {
"Within 15 miles": 10, [t("Within miles", { value: 15 })]: 10,
"Within 7.3 miles": 11, [t("Within miles", { value: 7.3 })]: 11,
"Within 3.6 miles": 12, [t("Within miles", { value: 3.6 })]: 12,
"Within 1.8 miles": 13, [t("Within miles", { value: 1.8 })]: 13,
"Within 0.9 miles": 14, [t("Within miles", { value: 0.9 })]: 14,
"Within 0.5 miles": 15, [t("Within miles", { value: 0.5 })]: 15,
"Within 0.2 miles": 16, [t("Within miles", { value: 0.2 })]: 16,
"Within 600 feet": 17, [t("Within feet", { value: 600 })]: 17,
"Within 300 feet": 18, [t("Within feet", { value: 300 })]: 18,
"Within 150 feet": 19, [t("Within feet", { value: 150 })]: 19,
}, },
}, },
disabledBy: [ disabledBy: [

19
src/components/PageComponents/ModuleConfig/NeighborInfo.tsx

@ -1,10 +1,12 @@
import { useDevice } from "@app/core/stores/deviceStore.ts"; import { useDevice } from "@app/core/stores/deviceStore.ts";
import type { NeighborInfoValidation } from "@app/validation/moduleConfig/neighborInfo.tsx"; import type { NeighborInfoValidation } from "@app/validation/moduleConfig/neighborInfo.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useTranslation } from "react-i18next";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
export const NeighborInfo = (): JSX.Element => { export const NeighborInfo = (): JSX.Element => {
const { moduleConfig, setWorkingModuleConfig } = useDevice(); const { moduleConfig, setWorkingModuleConfig } = useDevice();
const { t } = useTranslation();
const onSubmit = (data: NeighborInfoValidation) => { const onSubmit = (data: NeighborInfoValidation) => {
setWorkingModuleConfig( setWorkingModuleConfig(
@ -13,7 +15,7 @@ export const NeighborInfo = (): JSX.Element => {
case: "neighborInfo", case: "neighborInfo",
value: data, value: data,
}, },
}), })
); );
}; };
@ -23,21 +25,22 @@ export const NeighborInfo = (): JSX.Element => {
defaultValues={moduleConfig.neighborInfo} defaultValues={moduleConfig.neighborInfo}
fieldGroups={[ fieldGroups={[
{ {
label: "Neighbor Info Settings", label: t("Neighbor Info Settings"),
description: "Settings for the Neighbor Info module", description: t("Settings for the Neighbor Info module"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "enabled", name: "enabled",
label: "Enabled", label: t("Enabled"),
description: "Enable or disable Neighbor Info Module", description: t("Enable or disable Neighbor Info Module"),
}, },
{ {
type: "number", type: "number",
name: "updateInterval", name: "updateInterval",
label: "Update Interval", label: t("Update Interval"),
description: description: t(
"Interval in seconds of how often we should try to send our Neighbor Info to the mesh", "Interval in seconds of how often we should try to send our Neighbor Info to the mesh"
),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },

17
src/components/PageComponents/ModuleConfig/Paxcounter.tsx

@ -1,10 +1,12 @@
import type { PaxcounterValidation } from "@app/validation/moduleConfig/paxcounter.tsx"; import type { PaxcounterValidation } from "@app/validation/moduleConfig/paxcounter.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { useTranslation } from "react-i18next";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
export const Paxcounter = (): JSX.Element => { export const Paxcounter = (): JSX.Element => {
const { moduleConfig, setWorkingModuleConfig } = useDevice(); const { moduleConfig, setWorkingModuleConfig } = useDevice();
const { t } = useTranslation();
const onSubmit = (data: PaxcounterValidation) => { const onSubmit = (data: PaxcounterValidation) => {
setWorkingModuleConfig( setWorkingModuleConfig(
@ -13,7 +15,7 @@ export const Paxcounter = (): JSX.Element => {
case: "paxcounter", case: "paxcounter",
value: data, value: data,
}, },
}), })
); );
}; };
@ -23,21 +25,22 @@ export const Paxcounter = (): JSX.Element => {
defaultValues={moduleConfig.paxcounter} defaultValues={moduleConfig.paxcounter}
fieldGroups={[ fieldGroups={[
{ {
label: "Paxcounter Settings", label: t("Paxcounter Settings"),
description: "Settings for the Paxcounter module", description: "Settings for the Paxcounter module",
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "enabled", name: "enabled",
label: "Module Enabled", label: t("Module Enabled"),
description: "Enable Paxcounter", description: t("Enable Paxcounter"),
}, },
{ {
type: "number", type: "number",
name: "paxcounterUpdateInterval", name: "paxcounterUpdateInterval",
label: "Update Interval (seconds)", label: t("Update Interval (seconds)"),
description: description: t(
"How long to wait between sending paxcounter packets", "How long to wait between sending paxcounter packets"
),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },

20
src/components/PageComponents/ModuleConfig/RangeTest.tsx

@ -1,9 +1,11 @@
import type { RangeTestValidation } from "@app/validation/moduleConfig/rangeTest.tsx"; import type { RangeTestValidation } from "@app/validation/moduleConfig/rangeTest.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { useTranslation } from "react-i18next";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
export const RangeTest = (): JSX.Element => { export const RangeTest = (): JSX.Element => {
const { t } = useTranslation();
const { moduleConfig, setWorkingModuleConfig } = useDevice(); const { moduleConfig, setWorkingModuleConfig } = useDevice();
const onSubmit = (data: RangeTestValidation) => { const onSubmit = (data: RangeTestValidation) => {
@ -13,7 +15,7 @@ export const RangeTest = (): JSX.Element => {
case: "rangeTest", case: "rangeTest",
value: data, value: data,
}, },
}), })
); );
}; };
@ -23,20 +25,20 @@ export const RangeTest = (): JSX.Element => {
defaultValues={moduleConfig.rangeTest} defaultValues={moduleConfig.rangeTest}
fieldGroups={[ fieldGroups={[
{ {
label: "Range Test Settings", label: t("Range Test Settings"),
description: "Settings for the Range Test module", description: t("Settings for the Range Test module"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "enabled", name: "enabled",
label: "Module Enabled", label: t("Module Enabled"),
description: "Enable Range Test", description: t("Enable Range Test"),
}, },
{ {
type: "number", type: "number",
name: "sender", name: "sender",
label: "Message Interval", label: t("Message Interval"),
description: "How long to wait between sending test packets", description: t("How long to wait between sending test packets"),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },
@ -49,8 +51,8 @@ export const RangeTest = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "save", name: "save",
label: "Save CSV to storage", label: t("Save CSV to storage"),
description: "ESP32 Only", description: t("ESP32 Only"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",

51
src/components/PageComponents/ModuleConfig/Serial.tsx

@ -2,9 +2,11 @@ import type { SerialValidation } from "@app/validation/moduleConfig/serial.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
import { useTranslation } from "react-i18next";
export const Serial = (): JSX.Element => { export const Serial = (): JSX.Element => {
const { moduleConfig, setWorkingModuleConfig } = useDevice(); const { moduleConfig, setWorkingModuleConfig } = useDevice();
const { t } = useTranslation();
const onSubmit = (data: SerialValidation) => { const onSubmit = (data: SerialValidation) => {
setWorkingModuleConfig( setWorkingModuleConfig(
@ -13,7 +15,7 @@ export const Serial = (): JSX.Element => {
case: "serial", case: "serial",
value: data, value: data,
}, },
}), })
); );
}; };
@ -23,21 +25,22 @@ export const Serial = (): JSX.Element => {
defaultValues={moduleConfig.serial} defaultValues={moduleConfig.serial}
fieldGroups={[ fieldGroups={[
{ {
label: "Serial Settings", label: t("Serial Settings"),
description: "Settings for the Serial module", description: t("Settings for the Serial module"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "enabled", name: "enabled",
label: "Module Enabled", label: t("Module Enabled"),
description: "Enable Serial output", description: t("Enable Serial output"),
}, },
{ {
type: "toggle", type: "toggle",
name: "echo", name: "echo",
label: "Echo", label: t("Echo"),
description: description: t(
"Any packets you send will be echoed back to your device", "Any packets you send will be echoed back to your device"
),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -47,8 +50,10 @@ export const Serial = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "rxd", name: "rxd",
label: "Receive Pin", label: t("Receive Pin"),
description: "Set the GPIO pin to the RXD pin you have set up.", description: t(
"Set the GPIO pin to the RXD pin you have set up."
),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -58,8 +63,10 @@ export const Serial = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "txd", name: "txd",
label: "Transmit Pin", label: t("Transmit Pin"),
description: "Set the GPIO pin to the TXD pin you have set up.", description: t(
"Set the GPIO pin to the TXD pin you have set up."
),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -69,7 +76,7 @@ export const Serial = (): JSX.Element => {
{ {
type: "select", type: "select",
name: "baud", name: "baud",
label: "Baud Rate", label: t("Baud Rate"),
description: "The serial baud rate", description: "The serial baud rate",
disabledBy: [ disabledBy: [
@ -85,10 +92,11 @@ export const Serial = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "timeout", name: "timeout",
label: "Timeout", label: t("Timeout"),
description: description: t(
"Seconds to wait before we consider your packet as 'done'", "Seconds to wait before we consider your packet as 'done'"
),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -101,8 +109,8 @@ export const Serial = (): JSX.Element => {
{ {
type: "select", type: "select",
name: "mode", name: "mode",
label: "Mode", label: t("Mode"),
description: "Select Mode", description: t("Select Mode"),
disabledBy: [ disabledBy: [
{ {
@ -118,9 +126,10 @@ export const Serial = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "overrideConsoleSerialPort", name: "overrideConsoleSerialPort",
label: "Override Console Serial Port", label: t("Override Console Serial Port"),
description: description: t(
"If you have a serial port connected to the console, this will override it.", "If you have a serial port connected to the console, this will override it."
),
}, },
], ],
}, },

28
src/components/PageComponents/ModuleConfig/StoreForward.tsx

@ -1,10 +1,12 @@
import type { StoreForwardValidation } from "@app/validation/moduleConfig/storeForward.ts"; import type { StoreForwardValidation } from "@app/validation/moduleConfig/storeForward.ts";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { useTranslation } from "react-i18next";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
export const StoreForward = (): JSX.Element => { export const StoreForward = (): JSX.Element => {
const { moduleConfig, setWorkingModuleConfig } = useDevice(); const { moduleConfig, setWorkingModuleConfig } = useDevice();
const { t } = useTranslation();
const onSubmit = (data: StoreForwardValidation) => { const onSubmit = (data: StoreForwardValidation) => {
setWorkingModuleConfig( setWorkingModuleConfig(
@ -13,7 +15,7 @@ export const StoreForward = (): JSX.Element => {
case: "storeForward", case: "storeForward",
value: data, value: data,
}, },
}), })
); );
}; };
@ -23,20 +25,20 @@ export const StoreForward = (): JSX.Element => {
defaultValues={moduleConfig.storeForward} defaultValues={moduleConfig.storeForward}
fieldGroups={[ fieldGroups={[
{ {
label: "Store & Forward Settings", label: t("Store & Forward Settings"),
description: "Settings for the Store & Forward module", description: t("Settings for the Store & Forward module"),
fields: [ fields: [
{ {
type: "toggle", type: "toggle",
name: "enabled", name: "enabled",
label: "Module Enabled", label: t("Module Enabled"),
description: "Enable Store & Forward", description: t("Enable Store & Forward"),
}, },
{ {
type: "toggle", type: "toggle",
name: "heartbeat", name: "heartbeat",
label: "Heartbeat Enabled", label: t("Heartbeat Enabled"),
description: "Enable Store & Forward heartbeat", description: t("Enable Store & Forward heartbeat"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -46,8 +48,8 @@ export const StoreForward = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "records", name: "records",
label: "Number of records", label: t("Number of records"),
description: "Number of records to store", description: t("Number of records to store"),
disabledBy: [ disabledBy: [
{ {
@ -61,8 +63,8 @@ export const StoreForward = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "historyReturnMax", name: "historyReturnMax",
label: "History return max", label: t("History return max"),
description: "Max number of records to return", description: t("Max number of records to return"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",
@ -72,8 +74,8 @@ export const StoreForward = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "historyReturnWindow", name: "historyReturnWindow",
label: "History return window", label: t("History return window"),
description: "Max number of records to return", description: t("Max number of records to return"),
disabledBy: [ disabledBy: [
{ {
fieldName: "enabled", fieldName: "enabled",

50
src/components/PageComponents/ModuleConfig/Telemetry.tsx

@ -1,10 +1,12 @@
import type { TelemetryValidation } from "@app/validation/moduleConfig/telemetry.tsx"; import type { TelemetryValidation } from "@app/validation/moduleConfig/telemetry.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useTranslation } from "react-i18next";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
export const Telemetry = (): JSX.Element => { export const Telemetry = (): JSX.Element => {
const { moduleConfig, setWorkingModuleConfig } = useDevice(); const { moduleConfig, setWorkingModuleConfig } = useDevice();
const { t } = useTranslation();
const onSubmit = (data: TelemetryValidation) => { const onSubmit = (data: TelemetryValidation) => {
setWorkingModuleConfig( setWorkingModuleConfig(
@ -13,7 +15,7 @@ export const Telemetry = (): JSX.Element => {
case: "telemetry", case: "telemetry",
value: data, value: data,
}, },
}), })
); );
}; };
@ -23,14 +25,14 @@ export const Telemetry = (): JSX.Element => {
defaultValues={moduleConfig.telemetry} defaultValues={moduleConfig.telemetry}
fieldGroups={[ fieldGroups={[
{ {
label: "Telemetry Settings", label: t("Telemetry Settings"),
description: "Settings for the Telemetry module", description: t("Settings for the Telemetry module"),
fields: [ fields: [
{ {
type: "number", type: "number",
name: "deviceUpdateInterval", name: "deviceUpdateInterval",
label: "Query Interval", label: t("Query Interval"),
description: "Interval to get telemetry data", description: t("Interval to get telemetry data"),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },
@ -38,8 +40,8 @@ export const Telemetry = (): JSX.Element => {
{ {
type: "number", type: "number",
name: "environmentUpdateInterval", name: "environmentUpdateInterval",
label: "Update Interval", label: t("Update Interval"),
description: "How often to send Metrics over the mesh", description: t("How often to send Metrics over the mesh"),
properties: { properties: {
suffix: "Seconds", suffix: "Seconds",
}, },
@ -47,50 +49,52 @@ export const Telemetry = (): JSX.Element => {
{ {
type: "toggle", type: "toggle",
name: "environmentMeasurementEnabled", name: "environmentMeasurementEnabled",
label: "Module Enabled", label: t("Module Enabled"),
description: "Enable the Environment Telemetry", description: t("Enable the Environment Telemetry"),
}, },
{ {
type: "toggle", type: "toggle",
name: "environmentScreenEnabled", name: "environmentScreenEnabled",
label: "Displayed on Screen", label: t("Displayed on Screen"),
description: "Show the Telemetry Module on the OLED", description: t("Show the Telemetry Module on the OLED"),
}, },
{ {
type: "toggle", type: "toggle",
name: "environmentDisplayFahrenheit", name: "environmentDisplayFahrenheit",
label: "Display Fahrenheit", label: t("Display Fahrenheit"),
description: "Display temp in Fahrenheit", description: t("Display temp in Fahrenheit"),
}, },
{ {
type: "toggle", type: "toggle",
name: "airQualityEnabled", name: "airQualityEnabled",
label: "Air Quality Enabled", label: t("Air Quality Enabled"),
description: "Enable the Air Quality Telemetry", description: t("Enable the Air Quality Telemetry"),
}, },
{ {
type: "number", type: "number",
name: "airQualityInterval", name: "airQualityInterval",
label: "Air Quality Update Interval", label: t("Air Quality Update Interval"),
description: "How often to send Air Quality data over the mesh", description: t(
"How often to send Air Quality data over the mesh"
),
}, },
{ {
type: "toggle", type: "toggle",
name: "powerMeasurementEnabled", name: "powerMeasurementEnabled",
label: "Power Measurement Enabled", label: t("Power Measurement Enabled"),
description: "Enable the Power Measurement Telemetry", description: t("Enable the Power Measurement Telemetry"),
}, },
{ {
type: "number", type: "number",
name: "powerUpdateInterval", name: "powerUpdateInterval",
label: "Power Update Interval", label: t("Power Update Interval"),
description: "How often to send Power data over the mesh", description: t("How often to send Power data over the mesh"),
}, },
{ {
type: "toggle", type: "toggle",
name: "powerScreenEnabled", name: "powerScreenEnabled",
label: "Power Screen Enabled", label: t("Power Screen Enabled"),
description: "Enable the Power Telemetry Screen", description: t("Enable the Power Telemetry Screen"),
}, },
], ],
}, },

12
src/components/Sidebar.tsx

@ -2,6 +2,7 @@ import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.tsx";
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.tsx"; import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.tsx";
import { Subtle } from "@components/UI/Typography/Subtle.tsx"; import { Subtle } from "@components/UI/Typography/Subtle.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { useTranslation } from "react-i18next";
import type { Page } from "@core/stores/deviceStore.ts"; import type { Page } from "@core/stores/deviceStore.ts";
import { import {
BatteryMediumIcon, BatteryMediumIcon,
@ -24,6 +25,7 @@ export const Sidebar = ({ children }: SidebarProps): JSX.Element => {
const { hardware, nodes, metadata } = useDevice(); const { hardware, nodes, metadata } = useDevice();
const myNode = nodes.get(hardware.myNodeNum); const myNode = nodes.get(hardware.myNodeNum);
const myMetadata = metadata.get(0); const myMetadata = metadata.get(0);
const { t } = useTranslation();
const { activePage, setActivePage, setDialogOpen } = useDevice(); const { activePage, setActivePage, setDialogOpen } = useDevice();
interface NavLink { interface NavLink {
@ -34,27 +36,27 @@ export const Sidebar = ({ children }: SidebarProps): JSX.Element => {
const pages: NavLink[] = [ const pages: NavLink[] = [
{ {
name: "Messages", name: t("Messages"),
icon: MessageSquareIcon, icon: MessageSquareIcon,
page: "messages", page: "messages",
}, },
{ {
name: "Map", name: t("Map"),
icon: MapIcon, icon: MapIcon,
page: "map", page: "map",
}, },
{ {
name: "Config", name: t("Config"),
icon: SettingsIcon, icon: SettingsIcon,
page: "config", page: "config",
}, },
{ {
name: "Channels", name: t("Channels"),
icon: LayersIcon, icon: LayersIcon,
page: "channels", page: "channels",
}, },
{ {
name: "Nodes", name: t("Nodes"),
icon: UsersIcon, icon: UsersIcon,
page: "nodes", page: "nodes",
}, },

11
src/components/UI/Footer.tsx

@ -1,9 +1,10 @@
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next";
export interface FooterProps extends React.HTMLAttributes<HTMLElement> {} export interface FooterProps extends React.HTMLAttributes<HTMLElement> {}
const Footer = React.forwardRef<HTMLElement, FooterProps>( const Footer = React.forwardRef<HTMLElement, FooterProps>(
({ className, ...props }, ref) => { ({ className, ...props }, ref) => {
const { t } = useTranslation();
return ( return (
<footer <footer
className={`flex flex- justify-center p-2 ${className}`} className={`flex flex- justify-center p-2 ${className}`}
@ -18,20 +19,20 @@ const Footer = React.forwardRef<HTMLElement, FooterProps>(
className="hover:underline" className="hover:underline"
style={{ color: "var(--link)" }} style={{ color: "var(--link)" }}
> >
Powered by Vercel {t("Powered by ▲ Vercel")}
</a>{" "} </a>{" "}
| Meshtastic® is a registered trademark of Meshtastic LLC. |{" "} {t("| Meshtastic® is a registered trademark of Meshtastic LLC. |")}{" "}
<a <a
href="https://meshtastic.org/docs/legal" href="https://meshtastic.org/docs/legal"
className="hover:underline" className="hover:underline"
style={{ color: "var(--link)" }} style={{ color: "var(--link)" }}
> >
Legal Information {t("Legal Information")}
</a> </a>
</p> </p>
</footer> </footer>
); );
}, }
); );
export default Footer; export default Footer;

1
src/index.tsx

@ -3,6 +3,7 @@ import { enableMapSet } from "immer";
import "maplibre-gl/dist/maplibre-gl.css"; import "maplibre-gl/dist/maplibre-gl.css";
import { StrictMode } from "react"; import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import "./lang/i18n";
import { App } from "@app/App.tsx"; import { App } from "@app/App.tsx";

4
src/lang/de.d.ts

@ -0,0 +1,4 @@
declare module "./de.ts" {
const value: { [key: string]: any };
export default value;
}

421
src/lang/de.ts

@ -0,0 +1,421 @@
export default {
translation: {
"256, 128, or 8 bit PSKs allowed": "256, 128 oder 8 Bit PSKs erlaubt",
"| Meshtastic® is a registered trademark of Meshtastic LLC. |":
"| Meshtastic® ist eine eingetragene Marke von Meshtastic LLC. |",
"A unique name for the channel <12 bytes, leave blank for default":
"Ein einzigartiger Name für den Kanal <12 Bytes, leer lassen für Standard",
"Accent Color": "Akzentfarbe",
Active: "Aktiv",
"ADC Multiplier Override ratio":
"ADC Multiplikator Überschreibungsverhältnis",
"Address assignment selection": "Auswahl der Adresszuweisung",
"Are you sure you want to regenerate key pair?":
"Sind Sie sicher, dass Sie das Schlüsselpaar neu generieren möchten?",
"IP Address": "IP-Adresse",
"Add Channels": "Kanäle hinzufügen",
"Address of the INA219 battery monitor":
"Adresse des INA219-Batteriemonitors",
"Address Mode": "Adressmodus",
"Admin Key": "Admin-Schlüssel",
"Admin Settings": "Admin-Einstellungen",
"Air Quality Enabled": "Luftqualität aktiviert",
"Air Quality Update Interval": "Aktualisierungsintervall der Luftqualität",
"Alert Message": "Alarmnachricht",
"Alert Bell": "Alarmglocke",
"Alert Bell Buzzer": "Alarmglocken-Buzzer",
"Alert Bell Vibrate": "Alarmglocke vibrieren",
"Allow Input Source": "Eingangsquelle erlauben",
"Alert Message Buzzer": "Alarmnachricht Buzzer",
"Alert Message Vibrate": "Alarmnachricht vibrieren",
"Allow incoming device control over the insecure legacy admin channel":
"Eingehende Gerätesteuerung über den unsicheren alten Admin-Kanal erlauben",
"Allow Legacy Admin": "Alten Admin erlauben",
"Allow Position Requests": "Positionsanfragen erlauben",
"Ambient Lighting": "Umgebungslicht",
"Any packets you send will be echoed back to your device":
"Alle Pakete, die Sie senden, werden an Ihr Gerät zurückgesendet",
Application: "Anwendung",
Apply: "Anwenden",
"Approximate Location": "Ungefähre Position",
"Are you sure you want to remove this Node?":
"Sind Sie sicher, dass Sie diesen Knoten entfernen möchten?",
Audio: "Audio",
"Audio Settings": "Audioeinstellungen",
"Automatically shutdown node after this long when on battery, 0 for indefinite":
"Knoten automatisch nach dieser Zeit ausschalten, wenn er auf Batterie ist, 0 für unbegrenzt",
Bandwidth: "Bandbreite",
"Baud Rate": "Baudrate",
Bitrate: "Bitrate",
"Bitrate to use for audio encoding": "Bitrate für die Audio-Codierung",
BLE: "Bluetooth-Verbindung",
blue: "blau",
Bluetooth: "Bluetooth",
"Bluetooth Settings": "Bluetooth-Einstellungen",
"Bold Heading": "Fettgedruckter Titel",
"Bolden the heading text": "Den Titeltext fett formatieren",
"Boosted RX Gain": "Erhöhte RX-Verstärkung",
"Boosted RX gain": "Erhöhte RX-Verstärkung",
"Broadcast Interval": "Übertragungsintervall",
"Buzzer Pin": "Buzzer-Pin",
"Buzzer pin override": "Buzzer-Pin-Überschreibung",
"Button Pin": "Tasten-Pin",
"Button pin override": "Tasten-Pin-Überschreibung",
Canned: "Vorgedefinierte Nachricht",
"Carousel Delay": "Karussellverzögerung",
"Change Device Name": "Gerätename ändern",
"Canned Message Settings": "Einstellungen für vorgedefinierte Nachrichten",
"Channel bandwidth in MHz": "Kanalbandbreite in MHz",
"Channel Set/QR Code URL": "Kanal-Set/QR-Code-URL",
"Channel Settings": "Kanaleinstellungen",
Channels: "Kanäle",
"Clockwise event": "Uhrzeigersinn-Ereignis",
"Codec 2 Enabled": "Codec 2 aktiviert",
"Coding Rate": "Codierungsrate",
"Compass North Top": "Kompass-Nord oben",
Config: "Konfiguration",
"Configuration options for Position messages":
"Konfigurationsoptionen für Positionsnachrichten",
"Configure the external notification module":
"Externes Benachrichtigungsmodul konfigurieren",
"Configure whether device GPS is Enabled, Disabled, or Not Present":
"Konfigurieren, ob das GPS des Geräts aktiviert, deaktiviert oder nicht vorhanden ist",
"Connect New Device": "Neues Gerät verbinden",
"Connect New Node": "Neuen Knoten verbinden",
"Connect at least one device to get started":
"Verbinden Sie mindestens ein Gerät, um zu beginnen",
"Connected Devices": "Verbundenen Geräte",
"Connecting...": "Verbindung wird hergestellt...",
Connected: "Verbunden",
Connection: "Verbindung",
Contextual: "Kontextuell",
"Counter Clockwise event": "Gegenuhrzeigersinn-Ereignis",
"Coordinate display format": "Koordinatendarstellungsformat",
"Crypto, MQTT & misc settings": "Krypto, MQTT & verschiedene Einstellungen",
Current: "Aktuell",
Debug: "Debug",
"Default Gateway": "Standard-Gateway",
"Designate I²S Pin as Buzzer Output":
"I²S-Pin als Buzzer-Ausgang festlegen",
"Detection Sensor": "Erkennungssensor",
"Detection Sensor Settings": "Einstellungen des Erkennungssensors",
"Detection Triggered High": "Erkennung ausgelöst hoch",
Device: "Gerät",
"Device Settings": "Geräteinstellungen",
"Device telemetry is sent over PRIMARY. Only one PRIMARY allowed":
"Geräte-Telemetrie wird über PRIMARY gesendet. Nur ein PRIMARY erlaubt.",
"Disable Triple Click": "Doppelklick deaktivieren",
Disconnect: "Trennen",
Display: "Anzeige",
"Display metric or imperial units":
"Metrische oder imperiale Einheiten anzeigen",
"Display Mode": "Anzeigemodus",
"Display Units": "Anzeigeneinheiten",
"Display Settings": "Anzeigeeinstellungen",
"Display Fahrenheit": "Fahrenheit anzeigen",
"Display temp in Fahrenheit": "Temperatur in Fahrenheit anzeigen",
"Displayed on Screen": "Auf dem Bildschirm angezeigt",
DNS: "DNS",
"DNS Server": "DNS-Server",
"Don't report GPS position, but a manually-specified one":
"GPS-Position nicht melden, sondern eine manuell angegebene",
"Don't forward MQTT messages over the mesh":
"MQTT-Nachrichten nicht über das Mesh weiterleiten",
"Double Tap as Button Press": "Doppeltippen als Tastenanschlag",
"Downlink Enabled": "Downlink aktiviert",
Echo: "Echo",
Enabled: "Aktiviert",
"Enable or disable Bluetooth": "Bluetooth aktivieren oder deaktivieren",
"Enable or disable the WiFi radio":
"WiFi-Radio aktivieren oder deaktivieren",
"Unable to Connect:": "Keine Verbindung möglich:",
"Encoder Pin A": "Encoder-Pin A",
"Encoder Pin B": "Encoder-Pin B",
"Encoder Pin Press": "Encoder-Pin drücken",
"Enable power saving mode": "Energiesparmodus aktivieren",
"Enable Codec 2 audio encoding": "Codec 2 Audio-Codierung aktivieren",
"Enable Debug Log API": "Debug-Log-API aktivieren",
"Enable the up / down encoder": "Den Hoch-/Runter-Encoder aktivieren",
"Enable the rotary encoder": "Den Drehencoder aktivieren",
"Enable Canned Message": "Vorgedefinierte Nachricht aktivieren",
"Enable External Notification": "Externe Benachrichtigung aktivieren",
"Enable or disable map reporting":
"Kartenberichterstattung aktivieren oder deaktivieren",
"Enable or disable Neighbor Info Module":
"Nachbarinfo-Modul aktivieren oder deaktivieren",
"Enable or disable MQTT": "MQTT aktivieren oder deaktivieren",
"Enable Paxcounter": "Paxcounter aktivieren",
"Enable Range Test": "Reichweitentest aktivieren",
"Enable Serial output": "Serielle Ausgabe aktivieren",
"Enable the Air Quality Telemetry":
"Telemetrie zur Luftqualität aktivieren",
"Enable the Environment Telemetry": "Telemetrie zur Umwelt aktivieren",
"Enable the Power Telemetry Screen":
"Bildschirm für Leistungs-Telemetrie aktivieren",
"Enable the Power Measurement Telemetry":
"Telemetrie zur Leistungsmesung aktivieren",
"Enable or disable Detection Sensor Module":
"Erkennungssensor-Modul aktivieren oder deaktivieren",
"Enable Smart Position": "Intelligente Position aktivieren",
"Enable Store & Forward": "Speichern und Weiterleiten aktivieren",
"Enable Store & Forward heartbeat":
"Heartbeat für Speichern und Weiterleiten aktivieren",
"Enable/Disable transmit (TX) from the LoRa radio":
"Übertragung (TX) vom LoRa-Radio aktivieren/deaktivieren",
Encryption: "Verschlüsselung",
"Encryption Enabled": "Verschlüsselung aktiviert",
"Enable or disable TLS": "TLS aktivieren oder deaktivieren",
"Enable or disable MQTT encryption. Note: All messages are sent to the MQTT broker unencrypted if this option is not enabled, even when your uplink channels have encryption keys set. This includes position data.":
"MQTT-Verschlüsselung aktivieren oder deaktivieren. Hinweis: Alle Nachrichten werden unverschlüsselt an den MQTT-Broker gesendet, wenn diese Option nicht aktiviert ist, auch wenn Ihre Uplink-Kanäle Verschlüsselungsschlüssel gesetzt haben. Dazu gehören Positionsdaten.",
"Enable or disable the Ethernet port":
"Ethernet-Anschluss aktivieren oder deaktivieren",
"Enter Message": "Nachricht eingeben",
"ESP32 Only": "Nur ESP32",
"Ethernet Config": "Ethernet-Konfiguration",
"Ethernet port configuration": "Konfiguration des Ethernet-Anschlusses",
"Ext Notif": "Externe Benachrichtigung",
"External Notification Settings":
"Einstellungen für externe Benachrichtigungen",
"Factory Reset Config": "Werkseinstellungen zurücksetzen",
"Factory Reset Device": "Gerät auf Werkseinstellungen zurücksetzen",
"Flip display 180 degrees": "Display um 180 Grad drehen",
"Fix north to the top of compass": "Nordrichtung nach oben fixieren",
"Fixed Position": "Feste Position",
"Flip Screen": "Bildschirm umdrehen",
"Frequency Slot": "Frequenzslot",
"Frequency Offset": "Frequenzverschiebung",
"Frequency offset to correct for crystal calibration errors":
"Frequenzverschiebung zur Korrektur von Kristallkalibrierungsfehlern",
"Friendly Name": "Freundlicher Name",
Gateway: "Gateway",
Generator: "Generator",
"Generate QR Code": "QR-Code generieren",
Goto: "Gehe zu",
"GPS Display Units": "GPS-Anzeigeeinheiten",
"GPS module TX pin override": "GPS-Modul TX-Pin-Überschreibung",
"GPS module enable pin override":
"GPS-Modul Aktivierungs-Pin-Überschreibung",
"GPIO Pin Value (1-39) For encoder port A":
"GPIO-Pin-Wert (1-39) für Encoder-Port A",
"GPIO Pin Value (1-39) For encoder port B":
"GPIO-Pin-Wert (1-39) für Encoder-Port B",
"GPIO Pin Value (1-39) For encoder Press":
"GPIO-Pin-Wert (1-39) für Encoder-Druck",
"GPIO pin to use for PTT": "GPIO-Pin für PTT verwenden",
"GPS Update Interval": "GPS-Update-Intervall",
"GPS module RX pin override": "GPS-Modul RX-Pin-Überschreibung",
"GPS Mode": "GPS-Modus",
Green: "Grün",
"Heartbeat Enabled": "Heartbeat aktiviert",
"History return max": "Maximale Rückgabehistorie",
"History return window": "Historie Rückgabefenster",
"How fast to cycle through windows": "Wie schnell durch Fenster wechseln",
"Hop Limit": "Hop-Limit",
"How long the device will be in super deep sleep for":
"Wie lange das Gerät im Super-Tiefschlaf bleibt",
"How long the device will be in light sleep for":
"Wie lange das Gerät im Leichtschlaf bleibt",
"How long to wait between sending test packets":
"Wie lange zwischen dem Senden von Testpaketen gewartet werden soll",
"How long to wait between sending paxcounter packets":
"Wie lange zwischen dem Senden von Paxcounter-Paketen gewartet werden soll",
"How often to broadcast node info":
"Wie oft Knoteninformationen gesendet werden",
"How often to send Air Quality data over the mesh":
"Wie oft Luftqualitätsdaten über das Mesh gesendet werden",
"How often to send Metrics over the mesh":
"Wie oft Metriken über das Mesh gesendet werden",
"How often to send Power data over the mesh":
"Wie oft Leistungsdaten über das Mesh gesendet werden",
"How often to send position updates":
"Wie oft Positionsaktualisierungen gesendet werden",
"How often your position is sent out over the mesh":
"Wie oft Ihre Position über das Mesh gesendet wird",
"How often a GPS fix should be acquired":
"Wie oft ein GPS-Fix erlangt werden soll",
"How to handle rebroadcasting":
"Wie mit dem Rebroadcasting umgegangen werden soll",
HTTP: "HTTP",
"i2S WS": "i2S WS",
"GPIO pin to use for i2S WS": "GPIO-Pin für i2S WS verwenden",
"i2S SD": "i2S SD",
"GPIO pin to use for i2S SD": "GPIO-Pin für i2S SD verwenden",
"i2S SCK": "i2S SCK",
"GPIO pin to use for i2S SCK": "GPIO-Pin für i2S SCK verwenden",
"i2S DIN": "i2S DIN",
"GPIO pin to use for i2S DIN": "GPIO-Pin für i2S DIN verwenden",
"If not sharing precise location, position shared on channel will be accurate within this distance":
"Wenn keine präzise Position geteilt wird, ist die auf dem Kanal geteilte Position innerhalb dieser Distanz genau",
'If true, device is considered to be "managed" by a mesh administrator via admin messages':
"Bei true können Gerätekonfigurationsoptionen nur von einem Remote-Administratorknoten über Admin-Nachrichten aus der Ferne geändert werden. Aktivieren Sie diese Option nur, wenn ein geeigneter Remote-Admin-Knoten eingerichtet und der öffentliche Schlüssel im Feld unten gespeichert wurde.",
"If you have a serial port connected to the console, this will override it.":
"Wenn Sie einen seriellen Anschluss an die Konsole angeschlossen haben, wird dies überschrieben.",
"IP Address/Hostname": "IP-Adresse/Hostname",
"If the device does not receive a Bluetooth connection, the BLE radio will be disabled after this long":
"Wenn das Gerät keine Bluetooth-Verbindung erhält, wird das BLE-Radio nach dieser Zeit deaktiviert.",
"Ignore MQTT": "MQTT ignorieren",
Import: "Importieren",
"Import Channel Set": "Kanalset importieren",
"Indicates the number of chirps per symbol":
"Gibt die Anzahl der Pieptöne pro Symbol an",
Intervals: "Intervalle",
"INA219 Address": "INA219 Adresse",
"Interval to get telemetry data":
"Intervall zum Abrufen von Telemetriedaten",
"Interval in seconds of how often we should try to send our Neighbor Info to the mesh":
"Intervall in Sekunden, wie oft wir versuchen sollten, unsere Nachbarinformationen an das Mesh zu senden",
"Interval in seconds to publish map reports":
"Intervall in Sekunden zur Veröffentlichung von Kartenberichten",
IP: "IP",
"IP Config": "IP-Konfiguration",
"IP configuration": "IP-Konfiguration",
"JSON Enabled": "JSON aktiviert",
"LED State": "LED-Zustand",
"Legal Information": "Rechtliche Informationen",
"Last Heard": "Letzter Kontakt",
"Logging Settings": "Protokollierungseinstellungen",
LoRa: "LoRa",
"LoRa frequency channel number": "LoRa-Frequenzkanalnummer",
"Light Sleep Duration": "Dauer des Leichtschlafs",
"Long Name": "Langer Name",
"MAC Address": "MAC-Adresse",
Manage: "Verwalten",
Managed: "Verwaltet",
"Manage, connect and disconnect devices":
"Verwalten, verbinden und trennen von Geräten",
Map: "Karte",
"Map Report Publish Interval (s)":
"Veröffentlichungsintervall für Kartenberichte (s)",
"Map Reporting Enabled": "Kartenberichterstattung aktiviert",
"Max number of records to return":
"Maximale Anzahl der zurückzugebenden Datensätze",
"Max transmit power": "Maximale Sendeleistung",
"Maximum number of hops": "Maximale Anzahl der Hops",
"Mesh Settings": "Mesh-Einstellungen",
"Settings for the LoRa mesh": "Einstellungen für das LoRa-Mesh",
Messages: "Nachrichten",
"Message Interval": "Nachrichtenintervall",
"Minimum amount of time the device will stay awake for after receiving a packet":
"Minimale Zeit, die das Gerät nach dem Empfang eines Pakets wach bleibt",
"Minimum Broadcast Seconds": "Minimale Broadcast-Sekunden",
"Minimum distance (in meters) that must be traveled before a position update is sent":
"Minimale Entfernung (in Metern), die zurückgelegt werden muss, bevor eine Positionsaktualisierung gesendet wird",
"Minimum Wake Time": "Minimale Wachzeit",
"Minimum interval (in seconds) that must pass before a position update is sent":
"Minimales Intervall (in Sekunden), das vergehen muss, bevor eine Positionsaktualisierung gesendet wird",
Mode: "Modus",
Model: "Modell",
"Modem Preset": "Modem-Voreinstellung",
"Modem preset to use": "Verwendete Modem-Voreinstellung",
SNR: "SNR",
"Spreading Factor": "Breitbandfaktor",
SSID: "SSID",
"State Broadcast Seconds": "Zustandsübertragungssekunden",
"Select input event.": "Wählen Sie das Eingabeereignis.",
"Sets the current for the LED output. Default is 10":
"Setzt den Strom für den LED-Ausgang. Standard ist 10",
"Sets the red LED level. Values are 0-255":
"Setzt die rote LED-Stufe. Werte sind 0-255",
"Sets the green LED level. Values are 0-255":
"Setzt die grüne LED-Stufe. Werte sind 0-255",
"Sets the blue LED level. Values are 0-255":
"Setzt die blaue LED-Stufe. Werte sind 0-255",
"Settings for the LoRa waveform": "Einstellungen für die LoRa-Wellenform",
"Shutdown on battery delay": "Herunterfahren bei Batterieverzögerung",
"Sleep Settings": "Schlafmodus-Einstellungen",
"Sleep settings for the power module":
"Schlafmodus-Einstellungen für das Strommodul",
"Store & Forward Settings": "Speichern & Weiterleiten Einstellungen",
Subnet: "Subnetz",
"Subnet Mask": "Subnetzmaske",
"Super Deep Sleep Duration": "Super Deep Sleep Dauer",
"Switch Node": "Knoten umschalten",
Telemetry: "Telemetrie",
"Telemetry Settings": "Telemetrieeinstellungen",
"The Device will restart once the config is saved.":
"Das Gerät wird neu gestartet, sobald die Konfiguration gespeichert ist.",
"The denominator of the coding rate": "Der Nenner der Codierungsrate",
"The public key authorized to send admin messages to this node":
"Der öffentliche Schlüssel, der autorisiert ist, Administrationsnachrichten an diesen Knoten zu senden",
"The serial baud rate": "Die serielle Baudrate",
"This feature is not implemented yet":
"Dieses Feature ist noch nicht implementiert",
"The interval in seconds of how often we can send a message to the mesh when a state change is detected":
"Das Intervall in Sekunden, wie oft wir eine Nachricht an das Mesh senden können, wenn eine Zustandsänderung erkannt wird",
"The interval in seconds of how often we should send a message to the mesh with the current state regardless of changes":
"Das Intervall in Sekunden, wie oft wir eine Nachricht an das Mesh mit dem aktuellen Zustand unabhängig von Änderungen senden sollten",
"The GPIO pin to monitor for state changes":
"Der GPIO-Pin, der auf Zustandsänderungen überwacht wird",
"The current LoRa configuration will also be shared.":
"Die aktuelle LoRa-Konfiguration wird ebenfalls geteilt.",
"The current LoRa configuration will be overridden.":
"Die aktuelle LoRa-Konfiguration wird überschrieben.",
Timeout: "Zeitüberschreitung",
"TLS Enabled": "TLS aktiviert",
"Toggle Dark Mode": "Dunkelmodus umschalten",
"Transmit Enabled": "Übertragung aktiviert",
"Transmit Pin": "Übertragungs-Pin",
"Transmit Power": "Übertragungsleistung",
"Treat double tap as button press": "Doppelklick als Tastendruck behandeln",
"Turn off the display after this long":
"Schalte das Display nach dieser Zeit aus",
"Turn off the connected node after x minutes.":
"Schalte den verbundenen Knoten nach x Minuten aus.",
"Type of OLED screen attached to the device":
"Typ des an das Gerät angeschlossenen OLED-Bildschirms",
"Type a command or search...":
"Geben Sie einen Befehl oder eine Suche ein...",
Unknown: "Unbekannt",
"Unsuported connection method": "Nicht unterstützte Verbindungsmethode",
"Update Interval": "Aktualisierungsintervall",
"Update Interval (seconds)": "Aktualisierungsintervall (Sekunden)",
"Up Down enabled": "Hoch und runter aktiviert",
"Uplink Enabled": "Uplink aktiviert",
"Use I²S Pin as Buzzer": "I²S-Pin als Summer verwenden",
"Use one of the predefined modem presets":
"Verwenden Sie eines der vordefinierten Modem-Presets",
"Use Preset": "Preset verwenden",
"Use Preset?": "Preset verwenden?",
"Use Pullup": "Pullup verwenden",
"Use PWM": "PWM verwenden",
"Use TLS": "TLS verwenden",
"Used for tweaking battery voltage reading":
"Verwendet zur Anpassung der Batteriespannungsanzeige",
"Use the client's internet connection for MQTT (feature only active in mobile apps)":
"Verwenden Sie die Internetverbindung des Clients für MQTT (Funktion nur in mobilen Apps aktiv)",
"Used to create a shared key with a remote device":
"Verwendet, um einen gemeinsamen Schlüssel mit einem entfernten Gerät zu erstellen",
"Used to format the message sent to mesh, max 20 Characters":
"Verwendet zur Formatierung der an das Mesh gesendeten Nachricht, max. 20 Zeichen",
"Wake on Tap or Motion": "Aufwachen bei Tippen oder Bewegung",
"Web Bluetooth and Web Serial are currently only supported by Chromium-based browsers.":
"Web Bluetooth und Web Serial werden derzeit nur von Chromium-basierten Browsern unterstützt.",
"WebSerial is currently only supported by Chromium based browsers":
"WebSerial wird derzeit nur von Chromium-basierten Browsern unterstützt",
"Whether to send/consume JSON packets on MQTT":
"Ob JSON-Pakete über MQTT gesendet/empfangen werden sollen",
"[WIP] Clear Messages": "[WIP] Nachrichten löschen",
"OK to MQTT description":
"Wenn auf true gesetzt, zeigt diese Konfiguration an, dass der Benutzer dem Paket zustimmt, das zu MQTT hochgeladen wird. Wenn auf false gesetzt, wird entfernten Knoten aufgefordert, Pakete nicht an MQTT weiterzuleiten",
"Wake the device on tap or motion":
"Das Gerät bei Berührung oder Bewegung aufwecken",
"Waveform Settings": "Wellenform-Einstellungen",
"Web Bluetooth": "Web Bluetooth",
"Web Bluetooth is currently only supported by Chromium-based browsers":
"Web Bluetooth wird derzeit nur von Chromium-basierten Browsern unterstützt",
"Web Serial": "Web Serial",
"What role the device performs on the mesh":
"Welche Rolle das Gerät im Mesh spielt",
"Whether or not the GPIO pin state detection is triggered on HIGH (1), otherwise LOW (0)":
"Ob die GPIO-Pin-Zustandsdetektion bei HIGH (1) oder LOW (0) ausgelöst wird",
"Whether or not use INPUT_PULLUP mode for GPIO pin":
"Ob der INPUT_PULLUP-Modus für den GPIO-Pin verwendet werden soll",
"WiFi Config": "WiFi-Konfiguration",
"WiFi radio configuration": "WiFi-Radio-Konfiguration",
"Within feet": "Innerhalb von {{value}} Fuß",
"Within miles": "Innerhalb von {{value}} Meilen",
"Within m": "Innerhalb von {{value}} m",
"Within km": "Innerhalb von {{value}} km",
Yellow: "Gelb",
},
};

4
src/lang/en.d.ts

@ -0,0 +1,4 @@
declare module "./en.ts" {
const value: { [key: string]: any };
export default value;
}

570
src/lang/en.ts

@ -0,0 +1,570 @@
export default {
translation: {
"256, 128, or 8 bit PSKs allowed": "256, 128, or 8 bit PSKs allowed",
"| Meshtastic® is a registered trademark of Meshtastic LLC. |":
"| Meshtastic® is a registered trademark of Meshtastic LLC. |",
"A unique name for the channel <12 bytes, leave blank for default":
"A unique name for the channel <12 bytes, leave blank for default",
"Accent Color": "Accent Color",
Active: "Active",
"ADC Multiplier Override ratio": "ADC Multiplier Override ratio",
"Address assignment selection": "Address assignment selection",
"Are you sure you want to regenerate key pair?":
"Are you sure you want to regenerate key pair?",
"IP Address": "IP Address",
"Add Channels": "Add Channels",
"Address of the INA219 battery monitor":
"Address of the INA219 battery monitor",
"Address Mode": "Address Mode",
"Admin Key": "Admin Key",
"Admin Settings": "Admin Settings",
"Air Quality Enabled": "Air Quality Enabled",
"Air Quality Update Interval": "Air Quality Update Interval",
"Alert Message": "Alert Message",
"Alert Bell": "Alert Bell",
"Alert Bell Buzzer": "Alert Bell Buzzer",
"Alert Bell Vibrate": "Alert Bell Vibrate",
"Allow Input Source": "Allow Input Source",
"Alert Message Buzzer": "Alert Message Buzzer",
"Alert Message Vibrate": "Alert Message Vibrate",
"Allow incoming device control over the insecure legacy admin channel":
"Allow incoming device control over the insecure legacy admin channel",
"Allow Legacy Admin": "Allow Legacy Admin",
"Allow Position Requests": "Allow Position Requests",
"Ambient Lighting": "Ambient Lighting",
"Any packets you send will be echoed back to your device":
"Any packets you send will be echoed back to your device",
Application: "Application",
Apply: "Apply",
"Approximate Location": "Approximate Location",
"Are you sure you want to remove this Node?":
"Are you sure you want to remove this Node?",
Audio: "Audio",
"Audio Settings": "Audio Settings",
"Automatically shutdown node after this long when on battery, 0 for indefinite":
"Automatically shutdown node after this long when on battery, 0 for indefinite",
Bandwidth: "Bandwidth",
"Baud Rate": "Baud Rate",
Bitrate: "Bitrate",
"Bitrate to use for audio encoding": "Bitrate to use for audio encoding",
BLE: "Bluetooth connection",
blue: "blue",
Bluetooth: "Bluetooth",
"Bluetooth Settings": "Bluetooth Settings",
"Bold Heading": "Bold Heading",
"Bolden the heading text": "Bolden the heading text",
"Boosted RX Gain": "Boosted RX Gain",
"Boosted RX gain": "Boosted RX gain",
"Broadcast Interval": "Broadcast Interval",
"Buzzer Pin": "Buzzer Pin",
"Buzzer pin override": "Buzzer pin override",
"Button Pin": "Button Pin",
"Button pin override": "Button pin override",
Canned: "Canned Message",
"Carousel Delay": "Carousel Delay",
"Change Device Name": "Change Device Name",
"Canned Message Settings": "Canned Message Settings",
"Channel bandwidth in MHz": "Channel bandwidth in MHz",
"Channel Set/QR Code URL": "Channel Set/QR Code URL",
"Channel Settings": "Channel Settings",
Channels: "Channels",
"Clockwise event": "Clockwise event",
"Codec 2 Enabled": "Codec 2 Enabled",
"Coding Rate": "Coding Rate",
"Compass North Top": "Compass North Top",
Config: "Configuration",
"Configuration options for Position messages":
"Configuration options for Position messages",
"Configure the external notification module":
"Configure the external notification module",
"Configure whether device GPS is Enabled, Disabled, or Not Present":
"Configure whether device GPS is Enabled, Disabled, or Not Present",
"Connect New Device": "Connect New Device",
"Connect New Node": "Connect New Node",
"Connect at least one device to get started":
"Connect at least one device to get started",
"Connected Devices": "Connected Devices",
"Connecting...": "Connecting...",
Connected: "Connected",
Connection: "Connection",
Contextual: "Contextual",
"Counter Clockwise event": "Counter Clockwise event",
"Coordinate display format": "Coordinate display format",
"Crypto, MQTT & misc settings": "Crypto, MQTT & misc settings",
Current: "Current",
Debug: "Debug",
"Default Gateway": "Default Gateway",
"Designate I²S Pin as Buzzer Output": "Designate I²S Pin as Buzzer Output",
"Detection Sensor": "Detection Sensor",
"Detection Sensor Settings": "Detection Sensor Settings",
"Detection Triggered High": "Detection Triggered High",
Device: "Device",
"Device Settings": "Device Settings",
"Device telemetry is sent over PRIMARY. Only one PRIMARY allowed":
"Device telemetry is sent over PRIMARY. Only one PRIMARY allowed",
"Disable Triple Click": "Disable Triple Click",
Disconnect: "Disconnect",
Display: "Display",
"Display metric or imperial units": "Display metric or imperial units",
"Display Mode": "Display Mode",
"Display Units": "Display Units",
"Display Settings": "Display Settings",
"Display Fahrenheit": "Display Fahrenheit",
"Display temp in Fahrenheit": "Display temp in Fahrenheit",
"Displayed on Screen": "Displayed on Screen",
DNS: "DNS",
"DNS Server": "DNS Server",
"Don't report GPS position, but a manually-specified one":
"Don't report GPS position, but a manually-specified one",
"Don't forward MQTT messages over the mesh":
"Don't forward MQTT messages over the mesh",
"Double Tap as Button Press": "Double Tap as Button Press",
"Downlink Enabled": "Downlink Enabled",
Echo: "Echo",
Enabled: "Enabled",
"Enable or disable Bluetooth": "Enable or disable Bluetooth",
"Enable or disable the WiFi radio": "Enable or disable the WiFi radio",
"Unable to Connect:": "Unable to Connect:",
"Encoder Pin A": "Encoder Pin A",
"Encoder Pin B": "Encoder Pin B",
"Encoder Pin Press": "Encoder Pin Press",
"Enable power saving mode": "Enable power saving mode",
"Enable Codec 2 audio encoding": "Enable Codec 2 audio encoding",
"Enable Debug Log API": "Enable Debug Log API",
"Enable the up / down encoder": "Enable the up / down encoder",
"Enable the rotary encoder": "Enable the rotary encoder",
"Enable Canned Message": "Enable Canned Message",
"Enable External Notification": "Enable External Notification",
"Enable or disable map reporting": "Enable or disable map reporting",
"Enable or disable Neighbor Info Module":
"Enable or disable Neighbor Info Module",
"Enable or disable MQTT": "Enable or disable MQTT",
"Enable Paxcounter": "Enable Paxcounter",
"Enable Range Test": "Enable Range Test",
"Enable Serial output": "Enable Serial output",
"Enable the Air Quality Telemetry": "Enable the Air Quality Telemetry",
"Enable the Environment Telemetry": "Enable the Environment Telemetry",
"Enable the Power Telemetry Screen": "Enable the Power Telemetry Screen",
"Enable the Power Measurement Telemetry":
"Enable the Power Measurement Telemetry",
"Enable or disable Detection Sensor Module":
"Enable or disable Detection Sensor Module",
"Enable Smart Position": "Enable Smart Position",
"Enable Store & Forward": "Enable Store & Forward",
"Enable Store & Forward heartbeat": "Enable Store & Forward heartbeat",
"Enable/Disable transmit (TX) from the LoRa radio":
"Enable/Disable transmit (TX) from the LoRa radio",
Encryption: "Encryption",
"Encryption Enabled": "Encryption Enabled",
"Enable or disable TLS": "Enable or disable TLS",
"Enable or disable MQTT encryption. Note: All messages are sent to the MQTT broker unencrypted if this option is not enabled, even when your uplink channels have encryption keys set. This includes position data.":
"Enable or disable MQTT encryption. Note: All messages are sent to the MQTT broker unencrypted if this option is not enabled, even when your uplink channels have encryption keys set. This includes position data.",
"Enable or disable the Ethernet port":
"Enable or disable the Ethernet port",
"Enter Message": "Enter Message",
"ESP32 Only": "ESP32 Only",
"Ethernet Config": "Ethernet Config",
"Ethernet port configuration": "Ethernet port configuration",
"Ext Notif": "External Notification",
"External Notification Settings": "External Notification Settings",
"Factory Reset Config": "Factory Reset Config",
"Factory Reset Device": "Factory Reset Device",
"Flip display 180 degrees": "Flip display 180 degrees",
"Fix north to the top of compass": "Fix north to the top of compass",
"Fixed Position": "Fixed Position",
"Flip Screen": "Flip Screen",
"Frequency Slot": "Frequency Slot",
"Frequency Offset": "Frequency Offset",
"Frequency offset to correct for crystal calibration errors":
"Frequency offset to correct for crystal calibration errors",
"Friendly Name": "Friendly Name",
Gateway: "Gateway",
Generator: "Generator",
"Generate QR Code": "Generate QR Code",
Goto: "Goto",
"GPS Display Units": "GPS Display Units",
"GPS module TX pin override": "GPS module TX pin override",
"GPS module enable pin override": "GPS module enable pin override",
"GPIO Pin Value (1-39) For encoder port A":
"GPIO Pin Value (1-39) For encoder port A",
"GPIO Pin Value (1-39) For encoder port B":
"GPIO Pin Value (1-39) For encoder port B",
"GPIO Pin Value (1-39) For encoder Press":
"GPIO Pin Value (1-39) For encoder Press",
"GPIO pin to use for PTT": "GPIO pin to use for PTT",
"GPS Update Interval": "GPS Update Interval",
"GPS module RX pin override": "GPS module RX pin override",
"GPS Mode": "GPS Mode",
Green: "Green",
"Heartbeat Enabled": "Heartbeat Enabled",
"History return max": "History return max",
"History return window": "History return window",
"How fast to cycle through windows": "How fast to cycle through windows",
"Hop Limit": "Hop Limit",
"How long the device will be in super deep sleep for":
"How long the device will be in super deep sleep for",
"How long the device will be in light sleep for":
"How long the device will be in light sleep for",
"How long to wait between sending test packets":
"How long to wait between sending test packets",
"How long to wait between sending paxcounter packets":
"How long to wait between sending paxcounter packets",
"How often to broadcast node info": "How often to broadcast node info",
"How often to send Air Quality data over the mesh":
"How often to send Air Quality data over the mesh",
"How often to send Metrics over the mesh":
"How often to send Metrics over the mesh",
"How often to send Power data over the mesh":
"How often to send Power data over the mesh",
"How often to send position updates": "How often to send position updates",
"How often your position is sent out over the mesh":
"How often your position is sent out over the mesh",
"How often a GPS fix should be acquired":
"How often a GPS fix should be acquired",
"How to handle rebroadcasting": "How to handle rebroadcasting",
HTTP: "HTTP",
"i2S WS": "i2S WS",
"GPIO pin to use for i2S WS": "GPIO pin to use for i2S WS",
"i2S SD": "i2S SD",
"GPIO pin to use for i2S SD": "GPIO pin to use for i2S SD",
"i2S SCK": "i2S SCK",
"GPIO pin to use for i2S SCK": "GPIO pin to use for i2S SCK",
"i2S DIN": "i2S DIN",
"GPIO pin to use for i2S DIN": "GPIO pin to use for i2S DIN",
"If not sharing precise location, position shared on channel will be accurate within this distance":
"If not sharing precise location, position shared on channel will be accurate within this distance",
'If true, device is considered to be "managed" by a mesh administrator via admin messages':
"If true, device configuration options are only able to be changed remotely by a Remote Admin node via admin messages. Do not enable this option unless a suitable Remote Admin node has been setup, and the public key stored in the field below.",
"If you have a serial port connected to the console, this will override it.":
"If you have a serial port connected to the console, this will override it.",
"IP Address/Hostname": "IP Address/Hostname",
"If the device does not receive a Bluetooth connection, the BLE radio will be disabled after this long":
"If the device does not receive a Bluetooth connection, the BLE radio will be disabled after this long",
"Ignore MQTT": "Ignore MQTT",
Import: "Import",
"Import Channel Set": "Import Channel Set",
"Indicates the number of chirps per symbol":
"Indicates the number of chirps per symbol",
Intervals: "Intervals",
"INA219 Address": "INA219 Address",
"Interval to get telemetry data": "Interval to get telemetry data",
"Interval in seconds of how often we should try to send our Neighbor Info to the mesh":
"Interval in seconds of how often we should try to send our Neighbor Info to the mesh",
"Interval in seconds to publish map reports":
"Interval in seconds to publish map reports",
IP: "IP",
"IP Config": "IP Config",
"IP configuration": "IP configuration",
"JSON Enabled": "JSON Enabled",
"LED State": "LED State",
"Legal Information": "Legal Information",
"Last Heard": "Last Heard",
"Logging Settings": "Logging Settings",
LoRa: "LoRa",
"LoRa frequency channel number": "LoRa frequency channel number",
"Light Sleep Duration": "Light Sleep Duration",
"Long Name": "Long Name",
"MAC Address": "MAC Address",
Manage: "Manage",
Managed: "Managed",
"Manage, connect and disconnect devices":
"Manage, connect and disconnect devices",
Map: "Map",
"Map Report Publish Interval (s)": "Map Report Publish Interval (s)",
"Map Reporting Enabled": "Map Reporting Enabled",
"Max number of records to return": "Max number of records to return",
"Max transmit power": "Max transmit power",
"Maximum number of hops": "Maximum number of hops",
"Mesh Settings": "Mesh Settings",
"Settings for the LoRa mesh": "Settings for the LoRa mesh",
Messages: "Messages",
"Message Interval": "Message Interval",
"Minimum amount of time the device will stay awake for after receiving a packet":
"Minimum amount of time the device will stay awake for after receiving a packet",
"Minimum Broadcast Seconds": "Minimum Broadcast Seconds",
"Minimum distance (in meters) that must be traveled before a position update is sent":
"Minimum distance (in meters) that must be traveled before a position update is sent",
"Minimum Wake Time": "Minimum Wake Time",
"Minimum interval (in seconds) that must pass before a position update is sent":
"Minimum interval (in seconds) that must pass before a position update is sent",
Mode: "Mode",
Model: "Model",
"Modem Preset": "Modem Preset",
"Modem preset to use": "Modem preset to use",
"Node Info Broadcast Interval": "Node Info Broadcast Interval",
"Module Enabled": "Module Enabled",
"Monitor Pin": "Monitor Pin",
MQTT: "MQTT",
"MQTT Password": "MQTT Password",
"MQTT password to use for default/custom servers":
"MQTT password to use for default/custom servers",
"MQTT root topic to use for default/custom servers":
"MQTT root topic to use for default/custom servers",
"MQTT Server Address": "MQTT Server Address",
"MQTT server address to use for default/custom servers":
"MQTT server address to use for default/custom servers",
"MQTT Settings": "MQTT Settings",
"MQTT Username": "MQTT Username",
"MQTT username to use for default/custom servers":
"MQTT username to use for default/custom servers",
"Nag Timeout": "Nag Timeout",
Name: "Name",
"Neighbor Info": "Neighbor Info",
"Neighbor Info Settings": "Neighbor Info Settings",
network: "Network connection",
"Network name": "Network name",
"Network password": "Network password",
Never: "Never",
"New Connection": "New Connection",
"No Devices": "No Devices",
"New device": "New device",
"No Connection Bluetooth Disabled": "No Connection Bluetooth Disabled",
"No Messages": "No Messages",
"No devices paired yet.": "No devices paired yet.",
"No Traceroutes": "No Traceroutes",
Nodes: "Nodes",
"No results found.": "No results found.",
Now: "Now",
"NTP Config": "NTP Config",
"NTP configuration": "NTP configuration",
"NTP Server": "NTP Server",
"Number of records": "Number of records",
"Number of records to store": "Number of records to store",
"OK to MQTT": "OK to MQTT",
"OLED Type": "OLED Type",
"Only send position when there has been a meaningful change in location":
"Only send position when there has been a meaningful change in location",
Orange: "Orange",
Output: "Output",
"Output Buzzer": "Output Buzzer",
"Output live debug logging over serial, view and export position-redacted device logs over Bluetooth":
"Output live debug logging over serial, view and export position-redacted device logs over Bluetooth",
"Output MS": "Output MS",
"Output Vibrate": "Output Vibrate",
"Override Console Serial Port": "Override Console Serial Port",
"Override Duty Cycle": "Override Duty Cycle",
"Override Frequency": "Override Frequency",
"Override frequency": "Override frequency",
"Pairing mode": "Pairing mode",
Paxcounter: "Paxcounter",
"Paxcounter Settings": "Paxcounter Settings",
Pin: "Pin",
"Pin selection behaviour.": "Pin selection behaviour.",
"Pin to use when pairing": "Pin to use when pairing",
Pink: "Pink",
"Please enter a valid bit PSK.": "Please enter a valid {{count}} bit PSK.",
Position: "Position",
"Position Flags": "Position Flags",
"Position Settings": "Position Settings",
Power: "Power",
"Power Config": "Power Config",
"Powered by ▲ Vercel": "Powered by ▲ Vercel",
"Power Measurement Enabled": "Power Measurement Enabled",
"Power Screen Enabled": "Power Screen Enabled",
"Power Update Interval": "Power Update Interval",
"Precise Location": "Precise Location",
"pre-Shared Key": "pre-Shared Key",
"Press event": "Press event",
"Private Key": "Private Key",
"Proxy to Client Enabled": "Proxy to Client Enabled",
PSK: "PSK",
"PTT Pin": "PTT Pin",
"Public Key": "Public Key",
Purple: "Purple",
"Query Interval": "Query Interval",
"QR Code": "QR Code",
"Radio Settings": "Radio Settings",
"Range Test": "Range Test",
"Range Test Settings": "Range Test Settings",
"Read more:": "Read more:&nbsp;",
"Reboot the connected node after x minutes.":
"Reboot the connected node after x minutes.",
"Rebroadcast Mode": "Rebroadcast Mode",
"Receive Pin": "Receive Pin",
Reconfigure: "Reconfigure",
Red: "Red",
Regenerate: "Regenerate",
"Regenerate Key pair?": "Regenerate Key pair?",
Region: "Region",
Remove: "Remove",
"Remove Node?": "Remove Node?",
"Replace Channels": "Replace Channels",
"Reset Nodes": "Reset Nodes",
Role: "Role",
"Root topic": "Root topic",
"Rotary Encoder #1 Enabled": "Rotary Encoder #1 Enabled",
"Rsyslog Config": "Rsyslog Config",
"Rsyslog configuration": "Rsyslog configuration",
"Rsyslog Server": "Rsyslog Server",
"S&F": "S&F",
"Save CSV to storage": "Save CSV to storage",
"Schedule Reboot": "Schedule Reboot",
"Schedule Shutdown": "Schedule Shutdown",
"Screen layout variant": "Screen layout variant",
"Screen Timeout": "Screen Timeout",
"Seconds to wait before we consider your packet as 'done'":
"Seconds to wait before we consider your packet as 'done'",
Security: "Security",
"Security Settings": "Security Settings",
"Send Bell": "Send Bell",
"Sends a bell character with each message":
"Sends a bell character with each message",
"Send ASCII bell with alert message": "Send ASCII bell with alert message",
"Select if powered from a low-current source (i.e. solar), to minimize power consumption as much as possible.":
"Select if powered from a low-current source (i.e. solar), to minimize power consumption as much as possible.",
"Select Mode": "Select Mode",
"Send messages from MQTT to the local mesh":
"Send messages from MQTT to the local mesh",
"Send messages from the local mesh to MQTT":
"Send messages from the local mesh to MQTT",
"Sent out to other nodes on the mesh to allow them to compute a shared secret key":
"Sent out to other nodes on the mesh to allow them to compute a shared secret key",
"Send position to channel": "Send position to channel",
"Send precise location to channel": "Send precise location to channel",
Serial: "Serial connection",
"Serial Console over the Stream API": "Serial Console over the Stream API",
"Serial Output Enabled": "Serial Output Enabled",
"Serial Settings": "Serial Settings",
"Sets the region for your node": "Sets the region for your node",
"Sets LED to on or off": "Sets LED to on or off",
"Set the GPIO pin to the RXD pin you have set up.":
"Set the GPIO pin to the RXD pin you have set up.",
"Settings for the device": "Settings for the device",
"Settings for the LoRa radio": "Settings for the LoRa radio",
"Settings for Logging": "Settings for Logging",
"Settings for the Bluetooth module": "Settings for the Bluetooth module",
"Settings for the power module": "Settings for the power module",
"Settings for the Serial module": "Settings for the Serial module",
"Settings for the Store & Forward module":
"Settings for the Store & Forward module",
"Settings for the Telemetry module": "Settings for the Telemetry module",
"Settings for the Range Test module": "Settings for the Range Test module",
"Settings for the Neighbor Info module":
"Settings for the Neighbor Info module",
"Settings for the MQTT module": "Settings for the MQTT module",
"Settings for the Canned Message module":
"Settings for the Canned Message module",
"Settings for the Audio module": "Settings for the Audio module",
"Settings for the position module": "Settings for the position module",
"Show the Telemetry Module on the OLED":
"Show the Telemetry Module on the OLED",
"Settings for Admin": "Settings for Admin",
"Settings for the Detection Sensor module":
"Settings for the Detection Sensor module",
"Settings for the device display": "Settings for the device display",
"Sharable URL": "Sharable URL",
"Should an alert be triggered when receiving an incoming bell?":
"Should an alert be triggered when receiving an incoming bell?",
"Smart Position Minimum Distance": "Smart Position Minimum Distance",
"Smart Position Minimum Interval": "Smart Position Minimum Interval",
"Short Name": "Short Name",
SNR: "SNR",
"Spreading Factor": "Spreading Factor",
SSID: "SSID",
"State Broadcast Seconds": "State Broadcast Seconds",
"Select input event.": "Select input event.",
"Sets the current for the LED output. Default is 10":
"Sets the current for the LED output. Default is 10",
"Sets the red LED level. Values are 0-255":
"Sets the red LED level. Values are 0-255",
"Sets the green LED level. Values are 0-255":
"Sets the green LED level. Values are 0-255",
"Sets the blue LED level. Values are 0-255":
"Sets the blue LED level. Values are 0-255",
"Settings for the LoRa waveform": "Settings for the LoRa waveform",
"Shutdown on battery delay": "Shutdown on battery delay",
"Sleep Settings": "Sleep Settings",
"Sleep settings for the power module":
"Sleep settings for the power module",
"Store & Forward Settings": "Store & Forward Settings",
Subnet: "Subnet",
"Subnet Mask": "Subnet Mask",
"Super Deep Sleep Duration": "Super Deep Sleep Duration",
"Switch Node": "Switch Node",
Telemetry: "Telemetry",
"Telemetry Settings": "Telemetry Settings",
"The Device will restart once the config is saved.":
"The Device will restart once the config is saved.",
"The denominator of the coding rate": "The denominator of the coding rate",
"The public key authorized to send admin messages to this node":
"The public key authorized to send admin messages to this node",
"The serial baud rate": "The serial baud rate",
"This feature is not implemented yet":
"This feature is not implemented yet",
"The interval in seconds of how often we can send a message to the mesh when a state change is detected":
"The interval in seconds of how often we can send a message to the mesh when a state change is detected",
"The interval in seconds of how often we should send a message to the mesh with the current state regardless of changes":
"The interval in seconds of how often we should send a message to the mesh with the current state regardless of changes",
"The GPIO pin to monitor for state changes":
"The GPIO pin to monitor for state changes",
"The current LoRa configuration will also be shared.":
"The current LoRa configuration will also be shared.",
"The current LoRa configuration will be overridden.":
"The current LoRa configuration will be overridden.",
Timeout: "Timeout",
"TLS Enabled": "TLS Enabled",
"Toggle Dark Mode": "Toggle Dark Mode",
"Transmit Enabled": "Transmit Enabled",
"Transmit Pin": "Transmit Pin",
"Transmit Power": "Transmit Power",
"Treat double tap as button press": "Treat double tap as button press",
"Turn off the display after this long":
"Turn off the display after this long",
"Turn off the connected node after x minutes.":
"Turn off the connected node after x minutes.",
"Type of OLED screen attached to the device":
"Type of OLED screen attached to the device",
"Type a command or search...": "Type a command or search...",
Unknown: "Unknown",
"Unsuported connection method": "Unsuported connection method",
"Update Interval": "Update Interval",
"Update Interval (seconds)": "Update Interval (seconds)",
"Up Down enabled": "Up Down enabled",
"Uplink Enabled": "Uplink Enabled",
"Use I²S Pin as Buzzer": "Use I²S Pin as Buzzer",
"Use one of the predefined modem presets":
"Use one of the predefined modem presets",
"Use Preset": "Use Preset",
"Use Preset?": "Use Preset?",
"Use Pullup": "Use Pullup",
"Use PWM": "Use PWM",
"Use TLS": "Use TLS",
"Used for tweaking battery voltage reading":
"Used for tweaking battery voltage reading",
"Use the client's internet connection for MQTT (feature only active in mobile apps)":
"Use the client's internet connection for MQTT (feature only active in mobile apps)",
"Used to create a shared key with a remote device":
"Used to create a shared key with a remote device",
"Used to format the message sent to mesh, max 20 Characters":
"Used to format the message sent to mesh, max 20 Characters",
"Wake on Tap or Motion": "Wake on Tap or Motion",
"Web Bluetooth and Web Serial are currently only supported by Chromium-based browsers.":
"Web Bluetooth and Web Serial are currently only supported by Chromium-based browsers.",
"WebSerial is currently only supported by Chromium based browsers":
"WebSerial is currently only supported by Chromium based browsers: https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API#browser_compatibility",
"Whether to send/consume JSON packets on MQTT":
"Whether to send/consume JSON packets on MQTT",
"[WIP] Clear Messages": "[WIP] Clear Messages",
"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",
"Wake the device on tap or motion": "Wake the device on tap or motion",
"Waveform Settings": "Waveform Settings",
"Web Bluetooth": "Web Bluetooth",
"Web Bluetooth is currently only supported by Chromium-based browsers":
"Web Bluetooth is currently only supported by Chromium-based browsers",
"Web Serial": "Web Serial",
"What role the device performs on the mesh":
"What role the device performs on the mesh",
"Whether or not the GPIO pin state detection is triggered on HIGH (1), otherwise LOW (0)":
"Whether or not the GPIO pin state detection is triggered on HIGH (1), otherwise LOW (0)",
"Whether or not use INPUT_PULLUP mode for GPIO pin":
"Whether or not use INPUT_PULLUP mode for GPIO pin",
"WiFi Config": "WiFi Config",
"WiFi radio configuration": "WiFi radio configuration",
"Within feet": "Within {{value}} feet",
"Within miles": "Within {{value}} miles",
"Within m": "Within {{value}} m",
"Within km": "Within {{value}} km",
Yellow: "Yellow",
},
};

4
src/lang/fr.d.ts

@ -0,0 +1,4 @@
declare module "./fr.ts" {
const value: { [key: string]: any };
export default value;
}

600
src/lang/fr.ts

@ -0,0 +1,600 @@
export default {
translation: {
"256, 128, or 8 bit PSKs allowed": "256, 128, ou 8 bits PSK autorisés",
"| Meshtastic® is a registered trademark of Meshtastic LLC. |":
"| Meshtastic® est une marque déposée de Meshtastic LLC. |",
"A unique name for the channel <12 bytes, leave blank for default":
"Un nom unique pour le canal <12 octets, laisser vide pour le défaut",
"Accent Color": "Couleur d'accentuation",
Active: "Actif",
"ADC Multiplier Override ratio":
"Ratio de remplacement du multiplicateur ADC",
"Admin Key": "Clé d'administrateur",
"Admin Settings": "Paramètres d'administrateur",
"Add Channels": "Ajouter des canaux",
"Address assignment selection": "Sélection de l'attribution d'adresse",
"IP Address": "Adresse IP",
"Address of the INA219 battery monitor":
"Adresse du moniteur de batterie INA219",
"Address Mode": "Mode d'adressage",
"Air Quality Enabled": "Qualité de l'air activée",
"Air Quality Update Interval":
"Intervalle de mise à jour de la qualité de l'air",
"Alert Message": "Message d'alerte",
"Alert Bell": "Cloche d'alerte",
"Alert Bell Buzzer": "Buzzer de la cloche d'alerte",
"Alert Bell Vibrate": "Vibration de la cloche d'alerte",
"Allow Input Source": "Autoriser la source d'entrée",
"Allow incoming device control over the insecure legacy admin channel":
"Autoriser le contrôle des appareils entrants sur le canal d'administration hérité non sécurisé",
"Alert Message Buzzer": "Buzzer du message d'alerte",
"Alert Message Vibrate": "Vibration du message d'alerte",
"Allow Legacy Admin": "Autoriser l'administration héritée",
"Allow Position Requests": "Autoriser les demandes de position",
"Ambient Lighting": "Éclairage ambiant",
"Any packets you send will be echoed back to your device":
"Tous les paquets que vous envoyez seront renvoyés à votre appareil",
Application: "Application",
Apply: "Appliquer",
"Approximate Location": "Localisation approximative",
"Are you sure you want to regenerate key pair?":
"Êtes-vous sûr de vouloir régénérer la paire de clés ?",
"Are you sure you want to remove this Node?":
"Êtes-vous sûr de vouloir supprimer ce nœud ?",
Audio: "Audio",
"Audio Settings": "Paramètres audio",
"Automatically shutdown node after this long when on battery, 0 for indefinite":
"Arrêt automatique du nœud après ce laps de temps lorsqu'il est sur batterie, 0 pour indéfini",
Bandwidth: "Bande passante",
"Baud Rate": "Débit en bauds",
Bitrate: "Débit binaire",
"Bitrate to use for audio encoding":
"Débit binaire à utiliser pour le codage audio",
BLE: "Connexion Bluetooth",
blue: "bleu",
Bluetooth: "Bluetooth",
"Bluetooth Settings": "Paramètres Bluetooth",
"Bold Heading": "En-tête en gras",
"Bolden the heading text": "Mettre en gras le texte de l'en-tête",
"Boosted RX Gain": "Gain RX amplifié",
"Boosted RX gain": "Gain RX amplifié",
"Broadcast Interval": "Intervalle de diffusion",
"Buzzer Pin": "Broche du buzzer",
"Buzzer pin override": "Remplacement de la broche du buzzer",
"Button Pin": "Broche du bouton",
"Button pin override": "Remplacement de la broche du bouton",
Canned: "Message prédéfini",
"Canned Message Settings": "Paramètres du message prédéfini",
"Carousel Delay": "Délai du carrousel",
"Channel bandwidth in MHz": "Bande passante du canal en MHz",
"Channel Set/QR Code URL": "Ensemble de canaux/URL du code QR",
"Channel Settings": "Paramètres du canal",
Channels: "Canaux",
"Change Device Name": "Changer le nom de l'appareil",
"Clockwise event": "Événement dans le sens des aiguilles d'une montre",
"Codec 2 Enabled": "Codec 2 activé",
"Coding Rate": "Taux de codage",
"Compass North Top": "Nord en haut",
"Connect New Node": "Connecter un nouveau noeud",
Config: "Configuration",
"Configuration options for Position messages":
"Options de configuration pour les messages de position",
"Configure the external notification module":
"Configurer le module de notification externe",
"Configure whether device GPS is Enabled, Disabled, or Not Present":
"Configurez si le GPS de l'appareil est activé, désactivé ou non présent",
"Connect at least one device to get started":
"Connectez au moins un appareil pour commencer",
"Connected Devices": "Appareils connectés",
"Connect New Device": "Connecter un nouvel appareil",
"Connecting...": "Connexion...",
Connected: "Connecté",
Connection: "Connexion",
Contextual: "Contextuel",
"Counter Clockwise event":
"Événement dans le sens inverse des aiguilles d'une montre",
"Coordinate display format": "Format d'affichage des coordonnées",
Current: "Courant",
"Crypto, MQTT & misc settings": "Cryptage, MQTT et autres paramètres",
"Device telemetry is sent over PRIMARY. Only one PRIMARY allowed":
"Les données de télémétrie de l'appareil sont envoyées via PRIMARY. Un seul PRIMARY est autorisé",
Debug: "Débogage",
"Default Gateway": "Passerelle par défaut",
"Designate I²S Pin as Buzzer Output":
"Désigner la broche I²S comme sortie de buzzer",
"Detection Sensor": "Capteur de détection",
"Detection Sensor Settings": "Paramètres du capteur de détection",
"Detection Triggered High": "Détection déclenchée haute",
Device: "Appareil",
"Device Settings": "Paramètres de l'appareil",
"Disable Triple Click": "Désactiver le triple clic",
Disconnect: "Déconnecter",
Display: "Affichage",
"Display metric or imperial units":
"Afficher les unités métriques ou impériales",
"Display Mode": "Mode d'affichage",
"Display Units": "Unités d'affichage",
"Display Settings": "Paramètres d'affichage",
"Display Fahrenheit": "Affichage Fahrenheit",
"Display temp in Fahrenheit": "Affichage de la température en Fahrenheit",
"Displayed on Screen": "Affiché à l'écran",
DNS: "DNS",
"DNS Server": "Serveur DNS",
"Don't report GPS position, but a manually-specified one":
"Ne signalez pas la position GPS, mais une position spécifiée manuellement",
"Don't forward MQTT messages over the mesh":
"Ne pas transmettre les messages MQTT sur le maillage",
"Double Tap as Button Press": "Double Tap as Button Press",
"Downlink Enabled": "Transmission descendante activée",
Echo: "Écho",
Enabled: "Activé",
"Enable or disable Bluetooth": "Activer ou désactiver le Bluetooth",
"Enable or disable the WiFi radio": "Activer ou désactiver la radio WiFi",
"Unable to Connect:": "Impossible de se connecter :",
"Enable/Disable transmit (TX) from the LoRa radio":
"Activer/Désactiver la transmission (TX) depuis la radio LoRa",
"Encoder Pin A": "Broche de l'encodeur A",
"Encoder Pin B": "Broche de l'encodeur B",
"Encoder Pin Press": "Broche de l'encodeur Press",
"Enable power saving mode": "Activer le mode d'économie d'énergie",
"Enable Debug Log API": "Activer l'API de journal de débogage",
"Enable the up / down encoder": "Activer l'encodeur haut / bas",
"Enable Canned Message": "Activer le message prédéfini",
"Enable External Notification": "Activer la notification externe",
"Enable Codec 2 audio encoding": "Activer le codage audio Codec 2",
"Enable or disable map reporting":
"Activer ou désactiver le rapport de carte",
"Enable or disable TLS": "Activer ou désactiver TLS",
"Enable or disable Neighbor Info Module":
"Activer ou désactiver le module d'informations voisins",
"Enable or disable MQTT encryption. Note: All messages are sent to the MQTT broker unencrypted if this option is not enabled, even when your uplink channels have encryption keys set. This includes position data.":
"Activer ou désactiver le chiffrement MQTT. Remarque : Tous les messages sont envoyés au courtier MQTT non chiffrés si cette option n'est pas activée, même lorsque vos canaux de liaison montante ont des clés de chiffrement définies. Cela inclut les données de position.",
"Enable or disable MQTT": "Activer ou désactiver MQTT",
"Enable Paxcounter": "Activer Paxcounter",
"Enable Range Test": "Activer le test de portée",
"Enable Smart Position": "Activer la position intelligente",
"Enable the Air Quality Telemetry":
"Activer la télémétrie de la qualité de l'air",
"Enable the Environment Telemetry":
"Activer la télémétrie de l'environnement",
"Enable the Power Telemetry Screen":
"Activer l'écran de télémétrie de puissance",
"Enable the Power Measurement Telemetry":
"Activer la télémétrie de mesure de puissance",
"Enable Store & Forward": "Activer le stockage et la transmission",
"Enable Store & Forward heartbeat":
"Activer le battement de coeur de stockage et de transmission",
"Enable or disable Detection Sensor Module":
"Activer ou désactiver le module de capteur de détection",
"Enable or disable the Ethernet port":
"Activer ou désactiver le port Ethernet",
"Enable the rotary encoder": "Activer l'encodeur rotatif",
"Enter Message": "Entrer le message",
Encryption: "Chiffrement",
"Encryption Enabled": "Chiffrement activé",
"Ethernet Config": "Configuration Ethernet",
"Ethernet port configuration": "Configuration du port Ethernet",
"Ext Notif": "Notification externe",
"External Notification Settings": "Paramètres de notification externe",
"Enable Serial output": "Activer la sortie série",
"ESP32 Only": "ESP32 uniquement",
"Factory Reset Config": "Réinitialiser la configuration d'usine",
"Factory Reset Device": "Réinitialiser l'appareil aux paramètres d'usine",
"Fix north to the top of compass": "Fixer le nord en haut de la boussole",
"Fixed Position": "Position fixe",
"Flip display 180 degrees": "Retourner l'écran de 180 degrés",
"Flip Screen": "Retourner l'écran",
"Frequency Slot": "Plage de fréquence",
"Frequency Offset": "Décalage de fréquence",
"Frequency offset to correct for crystal calibration errors":
"Décalage de fréquence pour corriger les erreurs de calibration du cristal",
"Friendly Name": "Nom convivial",
Gateway: "Passerelle",
Generator: "Générateur",
"Generate QR Code": "Générer un code QR",
Goto: "Aller à",
"GPS Display Units": "Unités d'affichage GPS",
"GPIO Pin Value (1-39) For encoder port A":
"Valeur de la broche GPIO (1-39) pour le port A de l'encodeur",
"GPIO Pin Value (1-39) For encoder port B":
"Valeur de la broche GPIO (1-39) pour le port B de l'encodeur",
"GPIO Pin Value (1-39) For encoder Press":
"Valeur de la broche GPIO (1-39) pour l'encodeur Press",
"GPIO pin to use for PTT": "Broche GPIO à utiliser pour PTT",
"GPS module RX pin override": "Remplacement de la broche RX du module GPS",
"GPS Mode": "Mode GPS",
"GPS module TX pin override": "Remplacement de la broche TX du module GPS",
"GPS module enable pin override":
"Remplacement de la broche d'activation du module GPS",
"GPS Update Interval": "Intervalle de mise à jour GPS",
Green: "Vert",
"Heartbeat Enabled": "Battement de coeur activé",
"History return max": "Historique retour max",
"History return window": "Historique retour fenêtre",
"How fast to cycle through windows":
"Comment passer rapidement d'une fenêtre à l'autre",
"Hop Limit": "Limite de saut",
"How long the device will be in super deep sleep for":
"Combien de temps l'appareil sera en sommeil profond",
"How long to wait between sending test packets":
"Combien de temps attendre entre l'envoi des paquets de test",
"How long the device will be in light sleep for":
"Combien de temps l'appareil sera en sommeil léger",
"How long to wait between sending paxcounter packets":
"Combien de temps attendre entre l'envoi des paquets de paxcounter",
"How often to send Air Quality data over the mesh":
"Fréquence d'envoi des données de qualité de l'air sur le maillage",
"How often to broadcast node info":
"Fréquence de diffusion des informations de nœud",
"How often to send Metrics over the mesh":
"Fréquence d'envoi des métriques sur le maillage",
"How often to send Power data over the mesh":
"Fréquence d'envoi des données de puissance sur le maillage",
"How often to send position updates":
"Fréquence d'envoi des mises à jour de position",
"How often your position is sent out over the mesh":
"Fréquence à laquelle votre position est envoyée sur le maillage",
"How often a GPS fix should be acquired":
"Fréquence à laquelle un fix GPS doit être acquis",
"How to handle rebroadcasting": "Comment gérer la rediffusion",
HTTP: "HTTP",
"i2S WS": "i2S WS",
"GPIO pin to use for i2S WS": "Broche GPIO à utiliser pour i2S WS",
"i2S SD": "i2S SD",
"GPIO pin to use for i2S SD": "Broche GPIO à utiliser pour i2S SD",
"i2S SCK": "i2S SCK",
"GPIO pin to use for i2S SCK": "Broche GPIO à utiliser pour i2S SCK",
"i2S DIN": "i2S DIN",
"GPIO pin to use for i2S DIN": "Broche GPIO à utiliser pour i2S DIN",
"If not sharing precise location, position shared on channel will be accurate within this distance":
"Si la localisation précise n'est pas partagée, la position partagée sur le canal sera précise dans cette distance",
'If true, device is considered to be "managed" by a mesh administrator via admin messages':
"Si c'est vrai, les options de configuration de l'appareil ne peuvent être modifiées à distance par un nœud d'administration à distance via des messages d'administration. N'activez pas cette option à moins qu'un nœud d'administration à distance approprié ait été configuré et que la clé publique soit stockée dans le champ ci-dessous.",
"If you have a serial port connected to the console, this will override it.":
"Si vous avez un port série connecté à la console, cela le remplacera.",
"IP Address/Hostname": "Adresse IP/Nom d'hôte",
"If the device does not receive a Bluetooth connection, the BLE radio will be disabled after this long":
"Si l'appareil ne reçoit pas de connexion Bluetooth, la radio BLE sera désactivée après ce laps de temps",
"Ignore MQTT": "Ignorer MQTT",
Import: "Importer",
"Import Channel Set": "Importer un ensemble de canaux",
"INA219 Address": "Adresse INA219",
"Indicates the number of chirps per symbol":
"Indique le nombre de gazouillis par symbole",
Intervals: "Intervalles",
"Interval to get telemetry data":
"Intervalle pour obtenir les données de télémétrie",
"Interval in seconds of how often we should try to send our Neighbor Info to the mesh":
"Intervalle en secondes de la fréquence à laquelle nous devrions essayer d'envoyer nos informations de voisinage au maillage",
"Interval in seconds to publish map reports":
"Intervalle en secondes pour publier les rapports de carte",
IP: "IP",
"IP Config": "Configuration IP",
"IP configuration": "Configuration IP",
"JSON Enabled": "JSON activé",
"Last Heard": "Dernière connexion",
"LED State": "État de la LED",
"Legal Information": "Informations légales",
light: "Clair",
"Logging Settings": "Paramètres de journalisation",
"Long Name": "Nom long",
LoRa: "LoRa",
"LoRa frequency channel number": "Numéro de canal de fréquence LoRa",
"Light Sleep Duration": "Durée du sommeil léger",
"MAC Address": "Adresse MAC",
Manage: "Gérer",
Managed: "Géré",
"Manage, connect and disconnect devices":
"Gérer, connecter et déconnecter les appareils",
Map: "Carte",
"Map Reporting Enabled": "Rapport de carte activé",
"Map Report Publish Interval (s)":
"Intervalle de publication du rapport de carte (s)",
"Max number of records to return":
"Nombre maximal d'enregistrements à retourner",
"Max transmit power": "Puissance de transmission maximale",
"Maximum number of hops": "Nombre maximal de sauts",
"Mesh Settings": "Paramètres du maillage",
"Settings for the LoRa mesh": "Paramètres du maillage LoRa",
Messages: "Messages",
"Message Interval": "Intervalle de message",
"Minimum amount of time the device will stay awake for after receiving a packet":
"Durée minimale pendant laquelle l'appareil restera éveillé après avoir reçu un paquet",
"Minimum Broadcast Seconds": "Secondes de diffusion minimales",
"Minimum distance (in meters) that must be traveled before a position update is sent":
"Distance minimale (en mètres) qui doit être parcourue avant qu'une mise à jour de position ne soit envoyée",
"Minimum Wake Time": "Temps de réveil minimum",
"Minimum interval (in seconds) that must pass before a position update is sent":
"Intervalle minimum (en secondes) qui doit s'écouler avant qu'une mise à jour de position ne soit envoyée",
Mode: "Mode",
Model: "Modèle",
"Modem Preset": "Préréglage du modem",
"Modem preset to use": "Préréglage du modem à utiliser",
"Node Info Broadcast Interval":
"Intervalle de diffusion des informations du nœud",
"Module Enabled": "Module activé",
MQTT: "MQTT",
"MQTT Password": "Mot de passe MQTT",
"MQTT password to use for default/custom servers":
"Mot de passe MQTT à utiliser pour les serveurs par défaut/personnalisés",
"MQTT root topic to use for default/custom servers":
"Sujet racine MQTT à utiliser pour les serveurs par défaut/personnalisés",
"MQTT Server Address": "Adresse du serveur MQTT",
"MQTT server address to use for default/custom servers":
"Adresse du serveur MQTT à utiliser pour les serveurs par défaut/personnalisés",
"MQTT Settings": "Paramètres MQTT",
"MQTT Username": "Nom d'utilisateur MQTT",
"MQTT username to use for default/custom servers":
"Nom d'utilisateur MQTT à utiliser pour les serveurs par défaut/personnalisés",
"Nag Timeout": "Délai d'attente",
Name: "Nom",
"Neighbor Info": "Informations voisins",
"Neighbor Info Settings": "Paramètres des informations voisins",
network: "Connexion réseau",
"Network name": "Nom du réseau",
"Network password": "Mot de passe du réseau",
Never: "Jamais",
"New Connection": "Nouvelle connexion",
"New device": "Nouvel appareil",
"No Connection Bluetooth Disabled": "Pas de connexion Bluetooth désactivée",
"No Devices": "Aucun appareil",
Nodes: "Noeuds",
"Monitor Pin": "Broche de surveillance",
"No Messages": "Aucun message",
"No Traceroutes": "Aucun traceroute",
"No devices paired yet.": "Aucun appareil appairé pour le moment.",
"No results found.": "Aucun résultat trouvé.",
Now: "Maintenant",
"NTP Config": "Configuration NTP",
"NTP configuration": "Configuration du NTP",
"NTP Server": "Serveur NTP",
"Number of records": "Nombre d'enregistrements",
"Number of records to store": "Nombre d'enregistrements à stocker",
"OK to MQTT": "OK à MQTT",
"OLED Type": "Type d'OLED",
"Only send position when there has been a meaningful change in location":
"Envoyer la position uniquement lorsqu'il y a eu un changement significatif de position",
Orange: "Orange",
Output: "Sortie",
"Output Buzzer": "Buzzer de sortie",
"Output live debug logging over serial, view and export position-redacted device logs over Bluetooth":
"Journalisation de débogage en direct sur la sortie série, affichage et exportation des journaux d'appareils caviardés par position via Bluetooth",
"Output MS": "Sortie MS",
"Output Vibrate": "Vibration de sortie",
"Override Console Serial Port": "Remplacer le port série de la console",
"Override Duty Cycle": "Remplacer le cycle de service",
"Override Frequency": "Remplacer la fréquence",
"Override frequency": "Remplacer la fréquence",
"Pairing mode": "Mode d'appairage",
Paxcounter: "Paxcounter",
"Paxcounter Settings": "Paramètres de Paxcounter",
Pin: "Broche",
"Pin to use when pairing": "Broche à utiliser lors de l'appairage",
"Pin selection behaviour.": "Comportement de sélection de broche.",
Pink: "Rose",
"Please enter a valid bit PSK.":
"Veuillez entrer un PSK valide de {{count]} bits.",
Position: "Position",
"Position Flags": "Drapeaux de position",
"Position Settings": "Paramètres de position",
Power: "Alimentation",
"Power Config": "Configuration de l'alimentation",
"Powered by ▲ Vercel": "Propulsé par ▲ Vercel",
"Power Measurement Enabled": "Mesure de puissance activée",
"Power Screen Enabled": "Affichage de la puissance activé",
"Power Update Interval": "Intervalle de mise à jour de la puissance",
"Precise Location": "Localisation précise",
"pre-Shared Key": "Clé pré-partagée",
"Press event": "Événement de pression",
"Private Key": "Clé privée",
"Proxy to Client Enabled": "Proxy to Client Enabled",
PSK: "PSK",
"PTT Pin": "Broche PTT",
"Public Key": "Clé publique",
Purple: "Violet",
"Query Interval": "Intervalle de requête",
"QR Code": "QR Code",
"Radio Settings": "Paramètres radio",
"Range Test": "Test de portée",
"Range Test Settings": "Paramètres du test de portée",
"read more": "en savoir plus&nbsp;",
"Reboot the connected node after x minutes.":
"Redémarrer le nœud connecté après x minutes.",
"Rebroadcast Mode": "Mode de rebroadcast",
"Receive Pin": "Broche de réception",
Reconfigure: "Reconfigurer",
Red: "Rouge",
Regenerate: "Régénérer",
"Regenerate Key pair?": "Régénérer la paire de clés ?",
Region: "Région",
Remove: "Supprimer",
"Remove Node?": "Supprimer le noeud ?",
"Replace Channels": "Remplacer les canaux",
"Reset Nodes": "Réinitialiser les noeuds",
"Rsyslog Config": "Configuration Rsyslog",
"Rsyslog configuration": "Configuration Rsyslog",
"Rsyslog Server": "Serveur Rsyslog",
Role: "Rôle",
"Root topic": "Sujet racine",
"Rotary Encoder #1 Enabled": "Encodeur rotatif #1 activé",
"S&F": "S&F",
"Save CSV to storage": "Enregistrer le CSV dans le stockage",
"Schedule Reboot": "Planifier le redémarrage",
"Schedule Shutdown": "Planifier l'arrêt",
"Screen layout variant": "Variante de la disposition de l'écran",
"Screen Timeout": "Délai d'extinction de l'écran",
"Seconds to wait before we consider your packet as 'done'":
"Secondes à attendre avant de considérer votre paquet comme 'terminé'",
Security: "Sécurité",
"Security Settings": "Paramètres de sécurité",
"Send ASCII bell with alert message":
"Envoyer une cloche ASCII avec un message d'alerte",
"Select if powered from a low-current source (i.e. solar), to minimize power consumption as much as possible.":
"Sélectionnez si alimenté par une source de faible courant (par exemple solaire), pour minimiser la consommation d'énergie autant que possible.",
"Select Mode": "Sélectionner le mode",
"Select input event.": "Sélectionner l'événement d'entrée.",
"Send Bell": "Envoyer une cloche",
"Sends a bell character with each message":
"Envoie un caractère de cloche avec chaque message",
"Send messages from MQTT to the local mesh":
"Envoyer des messages de MQTT au maillage local",
"Send messages from the local mesh to MQTT":
"Envoyer des messages du maillage local à MQTT",
"Sent out to other nodes on the mesh to allow them to compute a shared secret key":
"Envoyé à d'autres nœuds sur le maillage pour leur permettre de calculer une clé secrète partagée",
"Send position to channel": "Envoyer la position au canal",
"Send precise location to channel":
"Envoyer la localisation précise au canal",
Serial: "Connexion série",
"Serial Console over the Stream API": "Console série via l'API de flux",
"Serial Output Enabled": "Sortie série activée",
"Serial Settings": "Paramètres série",
"Sets the region for your node": "Définit la région pour votre nœud",
"Sets LED to on or off": "Définit la LED sur marche ou arrêt",
"Sets the red LED level. Values are 0-255":
"Définit le niveau de la LED rouge. Les valeurs sont de 0 à 255",
"Sets the green LED level. Values are 0-255":
"Définit le niveau de la LED verte. Les valeurs sont de 0 à 255",
"Sets the blue LED level. Values are 0-255":
"Définit le niveau de la LED bleue. Les valeurs sont de 0 à 255",
"Sets the current for the LED output. Default is 10":
"Définit le courant pour la sortie LED. La valeur par défaut est 10",
"Set the GPIO pin to the RXD pin you have set up.":
"Définissez la broche GPIO sur la broche RXD que vous avez configurée.",
"Settings for the device display":
"Paramètres de l'affichage de l'appareil",
"Settings for the LoRa radio": "Paramètres de la radio LoRa",
"Settings for Logging": "Paramètres de journalisation",
"Settings for Admin": "Paramètres pour l'administrateur",
"Settings for the power module": "Paramètres pour le module d'alimentation",
"Settings for the Serial module": "Paramètres du module série",
"Settings for the position module": "Paramètres pour le module de position",
"Settings for the Store & Forward module":
"Paramètres du module de stockage et de transmission",
"Settings for the Telemetry module": "Paramètres du module de télémétrie",
"Settings for the Range Test module":
"Paramètres du module de test de portée",
"Settings for the Neighbor Info module":
"Paramètres du module d'informations voisins",
"Settings for the MQTT module": "Paramètres du module MQTT",
"Settings for the Detection Sensor module":
"Paramètres du module de capteur de détection",
"Settings for the Bluetooth module": "Paramètres du module Bluetooth",
"Settings for the Canned Message module":
"Paramètres du module de message prédéfini",
"Settings for the device": "Paramètres de l'appareil",
"Settings for the Audio module": "Paramètres du module audio",
"Settings for the LoRa waveform": "Paramètres de la forme d'onde LoRa",
"Sharable URL": "URL partageable",
"Show the Telemetry Module on the OLED":
"Afficher le module de télémétrie sur l'OLED",
"Should an alert be triggered when receiving an incoming bell?":
"Un alerte doit-elle être déclenchée lors de la réception d'une cloche entrante ?",
"Short Name": "Nom court",
"Shutdown on battery delay": "Arrêt sur délai de batterie",
"Sleep Settings": "Paramètres de veille",
"Sleep settings for the power module":
"Paramètres de veille pour le module d'alimentation",
"Smart Position Minimum Distance":
"Distance minimale de la position intelligente",
"Smart Position Minimum Interval":
"Intervalle minimum de la position intelligente",
SNR: "SNR",
"Spreading Factor": "Facteur de propagation",
"State Broadcast Seconds": "Secondes de diffusion d'état",
"Store & Forward Settings": "Paramètres de stockage et de transmission",
Subnet: "Sous-réseau",
"Subnet Mask": "Masque de sous-réseau",
"Super Deep Sleep Duration": "Durée de sommeil super profond",
SSID: "SSID",
"Switch Node": "Changer de noeud",
Telemetry: "Télémétrie",
"Telemetry Settings": "Paramètres de télémétrie",
"The Device will restart once the config is saved.":
"L'appareil redémarrera une fois la configuration enregistrée.",
"The denominator of the coding rate": "Le dénominateur du taux de codage",
"The public key authorized to send admin messages to this node":
"La clé publique autorisée à envoyer des messages d'administration à ce nœud",
"The serial baud rate": "Le débit en bauds série",
"The interval in seconds of how often we can send a message to the mesh when a state change is detected":
"L'intervalle en secondes de la fréquence à laquelle nous pouvons envoyer un message au maillage lorsqu'un changement d'état est détecté",
"The interval in seconds of how often we should send a message to the mesh with the current state regardless of changes":
"L'intervalle en secondes de la fréquence à laquelle nous devrions envoyer un message au maillage avec l'état actuel, indépendamment des changements",
"This feature is not implemented yet":
"Cette fonctionnalité n'est pas encore implémentée",
"The GPIO pin to monitor for state changes":
"La broche GPIO à surveiller pour les changements d'état",
"The current LoRa configuration will also be shared.":
"La configuration LoRa actuelle sera également partagée.",
"The current LoRa configuration will be overridden.":
"La configuration LoRa actuelle sera remplacée.",
Timeout: "Délai d'attente",
"TLS Enabled": "TLS activé",
"Toggle Dark Mode": "Basculer le mode sombre/clair",
"Transmit Enabled": "Transmission activée",
"Transmit Pin": "Broche de transmission",
"Transmit Power": "Puissance de transmission",
"Treat double tap as button press":
"Traiter le double tapotement comme une pression sur un bouton",
"Turn off the display after this long":
"Éteindre l'écran après ce laps de temps",
"Turn off the connected node after x minutes.":
"Éteindre le nœud connecté après x minutes.",
"Type of OLED screen attached to the device":
"Type d'écran OLED attaché à l'appareil",
"Type a command or search...": "Tapez une commande ou une recherche...",
"Update Interval": "Intervalle de mise à jour",
"Update Interval (seconds)": "Intervalle de mise à jour (secondes)",
"Up Down enabled": "Haut Bas activé",
Unknown: "Inconnu",
"Unsuported connection method": "Méthode de connexion non prise en charge",
"Uplink Enabled": "Transmission montante activée",
"Use I²S Pin as Buzzer": "Utiliser la broche I²S comme buzzer",
"Used for tweaking battery voltage reading":
"Utilisé pour ajuster la lecture de la tension de la batterie",
"Use Preset": "Utiliser le préréglage",
"Use Preset?": "Utiliser le préréglage ?",
"Use one of the predefined modem presets":
"Utiliser l'un des préréglages de modem",
"Use Pullup": "Utiliser le pullup",
"Use PWM": "Utiliser le PWM",
"Use TLS": "Utiliser TLS",
"Use the client's internet connection for MQTT (feature only active in mobile apps)":
"Utiliser la connexion Internet du client pour MQTT (fonctionnalité uniquement active dans les applications mobiles)",
"Used to format the message sent to mesh, max 20 Characters":
"Utilisé pour formater le message envoyé au maillage, max 20 caractères",
"Used to create a shared key with a remote device":
"Utilisé pour créer une clé partagée avec un appareil distant",
"OK to MQTT description":
"Lorsqu'il est défini sur true, cette configuration indique que l'utilisateur approuve le paquet à télécharger sur MQTT. Si défini sur false, il est demandé aux nœuds distants de ne pas transférer les paquets à MQTT",
"Wake on Tap or Motion": "Réveil sur tapotement ou mouvement",
"Wake the device on tap or motion":
"Réveiller l'appareil sur tapotement ou mouvement",
"Wake on Tap": "Réveil sur tapotement",
"Waveform Settings": "Paramètres de forme d'onde",
"Web Bluetooth": "Web Bluetooth",
"Web Bluetooth and Web Serial are currently only supported by Chromium-based browsers.":
"Le Web Bluetooth et le Web Serial sont actuellement pris en charge uniquement par les navigateurs basés sur Chromium.",
"Web Serial": "Web Serial",
"WebSerial is currently only supported by Chromium based browsers":
"WebSerial est actuellement pris en charge uniquement par les navigateurs basés sur Chromium : https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API#browser_compatibility",
"Web Bluetooth is currently only supported by Chromium-based browsers":
"Le Web Bluetooth est actuellement pris en charge uniquement par les navigateurs basés sur Chromium",
"What role the device performs on the mesh":
"Le rôle que l'appareil joue sur le maillage",
"Whether to send/consume JSON packets on MQTT":
"Envoyer/consommer des paquets JSON sur MQTT",
"Whether or not the GPIO pin state detection is triggered on HIGH (1), otherwise LOW (0)":
"Détermine si la détection de l'état de la broche GPIO est déclenchée sur HIGH (1), sinon LOW (0)",
"Whether or not use INPUT_PULLUP mode for GPIO pin":
"Utiliser ou non le mode INPUT_PULLUP pour la broche GPIO",
"WiFi Config": "Configuration WiFi",
"WiFi radio configuration": "Configuration de la radio WiFi",
"Within feet": "Dans un rayon de {{value}} pieds",
"Within miles": "Dans un rayon de {{value}} mi les",
"Within m": "Dans un rayon de {{value}} m",
"Within km": "Dans un rayon de {{value}} km",
"[WIP] Clear Messages": "[WIP] Effacer les messages",
Yellow: "Jaune",
},
};

28
src/lang/i18n.ts

@ -0,0 +1,28 @@
import i18n from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import { initReactI18next } from "react-i18next";
import en from "./en.ts";
import fr from "./fr.ts";
import it from "./it.ts";
import de from "./de.ts";
// the translations
// (tip move them in a JSON file and import them,
// or even better, manage them separated from your code: https://react.i18next.com/guides/multiple-translation-files)
const resources = {
en,
fr,
it,
de,
};
i18n
.use(initReactI18next) // passes i18n down to react-i18next
.use(LanguageDetector)
.init({
resources,
interpolation: {
escapeValue: false, // react already safes from xss
},
});
export default i18n;

4
src/lang/it.d.ts

@ -0,0 +1,4 @@
declare module "./it.ts" {
const value: { [key: string]: any };
export default value;
}

596
src/lang/it.ts

@ -0,0 +1,596 @@
export default {
translation: {
"256, 128, or 8 bit PSKs allowed": "PSK consentiti di 256, 128 o 8 bit",
"| Meshtastic® is a registered trademark of Meshtastic LLC. |":
"| Meshtastic® è un marchio registrato di Meshtastic LLC. |",
"A unique name for the channel <12 bytes, leave blank for default":
"Un nome unico per il canale <12 byte, lascia vuoto per predefinito",
"Accent Color": "Colore di Accento",
Active: "Attivo",
"ADC Multiplier Override ratio":
"Rapporto di Override del Moltiplicatore ADC",
"Address assignment selection":
"Selezione dell'assegnazione dell'indirizzo",
"Are you sure you want to regenerate key pair?":
"Sei sicuro di voler rigenerare la coppia di chiavi?",
"IP Address": "Indirizzo IP",
"Add Channels": "Aggiungi Canali",
"Address of the INA219 battery monitor":
"Indirizzo del monitor della batteria INA219",
"Address Mode": "Modalità Indirizzo",
"Admin Key": "Chiave Amministratore",
"Admin Settings": "Impostazioni Amministratore",
"Air Quality Enabled": "Qualità dell'Aria Abilitata",
"Air Quality Update Interval":
"Intervallo di Aggiornamento della Qualità dell'Aria",
"Alert Message": "Messaggio di Allerta",
"Alert Bell": "Campanello di Allerta",
"Alert Bell Buzzer": "Buzzer del Campanello di Allerta",
"Alert Bell Vibrate": "Vibrazione del Campanello di Allerta",
"Allow Input Source": "Consenti Sorgente di Ingressi",
"Alert Message Buzzer": "Buzzer del Messaggio di Allerta",
"Alert Message Vibrate": "Vibrazione del Messaggio di Allerta",
"Allow incoming device control over the insecure legacy admin channel":
"Consenti il controllo del dispositivo in entrata tramite il canale amministrativo legacy insicuro",
"Allow Legacy Admin": "Consenti Amministratore Legacy",
"Allow Position Requests": "Consenti Richieste di Posizione",
"Ambient Lighting": "Illuminazione Ambientale",
"Any packets you send will be echoed back to your device":
"Qualsiasi pacchetto invii verrà inviato di nuovo al tuo dispositivo",
Application: "Applicazione",
Apply: "Applica",
"Approximate Location": "Posizione Approssimativa",
"Are you sure you want to remove this Node?":
"Sei sicuro di voler rimuovere questo Nodo?",
Audio: "Audio",
"Audio Settings": "Impostazioni Audio",
"Automatically shutdown node after this long when on battery, 0 for indefinite":
"Spegnere automaticamente il nodo dopo questo tempo quando è a batteria, 0 per indefinito",
Bandwidth: "Larghezza di Banda",
"Baud Rate": "Velocità di Baud",
Bitrate: "Bitrate",
"Bitrate to use for audio encoding":
"Bitrate da utilizzare per la codifica audio",
BLE: "Connessione Bluetooth",
blue: "blu",
Bluetooth: "Bluetooth",
"Bluetooth Settings": "Impostazioni Bluetooth",
"Bold Heading": "Titolo in Grassetto",
"Bolden the heading text": "Rendi il testo del titolo in grassetto",
"Boosted RX Gain": "Guadagno RX Potenziato",
"Boosted RX gain": "Guadagno RX Potenziato",
"Broadcast Interval": "Intervallo di Trasmissione",
"Buzzer Pin": "Pin del Buzzer",
"Buzzer pin override": "Override del pin del Buzzer",
"Button Pin": "Pin del Pulsante",
"Button pin override": "Override del pin del Pulsante",
Canned: "Messaggio Predefinito",
"Carousel Delay": "Ritardo Carousel",
"Change Device Name": "Cambia Nome Dispositivo",
"Canned Message Settings": "Impostazioni Messaggio Predefinito",
"Channel bandwidth in MHz": "Larghezza di Banda del Canale in MHz",
"Channel Set/QR Code URL": "Set Canale/URL QR Code",
"Channel Settings": "Impostazioni Canale",
Channels: "Canali",
"Clockwise event": "Evento in senso orario",
"Codec 2 Enabled": "Codec 2 Abilitato",
"Coding Rate": "Tasso di Codifica",
"Compass North Top": "Nord della Bussola in Alto",
Config: "Configurazione",
"Configuration options for Position messages":
"Opzioni di configurazione per i messaggi di Posizione",
"Configure the external notification module":
"Configura il modulo di notifica esterno",
"Configure whether device GPS is Enabled, Disabled, or Not Present":
"Configura se il GPS del dispositivo è Abilitato, Disabilitato o Non Presente",
"Connect New Device": "Collega Nuovo Dispositivo",
"Connect New Node": "Collega Nuovo Nodo",
"Connect at least one device to get started":
"Collega almeno un dispositivo per iniziare",
"Connected Devices": "Dispositivi Connessi",
"Connecting...": "Connessione in corso...",
Connected: "Connesso",
Connection: "Connessione",
Contextual: "Contestuale",
"Counter Clockwise event": "Evento in senso antiorario",
"Coordinate display format": "Formato di visualizzazione delle Coordinate",
"Crypto, MQTT & misc settings": "Impostazioni Crypto, MQTT & varie",
Current: "Corrente",
Debug: "Debug",
"Default Gateway": "Gateway Predefinito",
"Designate I²S Pin as Buzzer Output":
"Designa il Pin I²S come Uscita per Buzzer",
"Detection Sensor": "Sensore di Rilevamento",
"Detection Sensor Settings": "Impostazioni Sensore di Rilevamento",
"Detection Triggered High": "Rilevamento Attivato Alto",
Device: "Dispositivo",
"Device Settings": "Impostazioni Dispositivo",
"Device telemetry is sent over PRIMARY. Only one PRIMARY allowed":
"La telemetria del dispositivo viene inviata su PRIMARY. È consentito solo un PRIMARY",
"Disable Triple Click": "Disabilita Triplo Click",
Disconnect: "Disconnetti",
Display: "Display",
"Display metric or imperial units": "Visualizza unità metriche o imperiali",
"Display Mode": "Modalità Display",
"Display Units": "Unità di Visualizzazione",
"Display Settings": "Impostazioni di Visualizzazione",
"Display Fahrenheit": "Visualizza Fahrenheit",
"Display temp in Fahrenheit": "Visualizza temperatura in Fahrenheit",
"Displayed on Screen": "Visualizzato sullo Schermo",
DNS: "DNS",
"DNS Server": "Server DNS",
"Don't report GPS position, but a manually-specified one":
"Non riportare la posizione GPS, ma una specificata manualmente",
"Don't forward MQTT messages over the mesh":
"Non inoltrare i messaggi MQTT sulla rete mesh",
"Double Tap as Button Press": "Doppio Tap come Pressione di Pulsante",
"Downlink Enabled": "Downlink Abilitato",
Echo: "Eco",
Enabled: "Abilitato",
"Enable or disable Bluetooth": "Abilita o disabilita Bluetooth",
"Enable or disable the WiFi radio": "Abilita o disabilita la radio WiFi",
"Unable to Connect:": "Impossibile Connettersi:",
"Encoder Pin A": "Pin Encoder A",
"Encoder Pin B": "Pin Encoder B",
"Encoder Pin Press": "Pressione del Pin Encoder",
"Enable power saving mode": "Abilita la modalità di risparmio energetico",
"Enable Codec 2 audio encoding": "Abilita la codifica audio Codec 2",
"Enable Debug Log API": "Abilita API del Log di Debug",
"Enable the up / down encoder": "Abilita l'encoder su/giù",
"Enable the rotary encoder": "Abilita l'encoder rotativo",
"Enable Canned Message": "Abilita Messaggio Predefinito",
"Enable External Notification": "Abilita Notifica Esterna",
"Enable or disable map reporting":
"Abilita o disabilita la segnalazione della mappa",
"Enable or disable Neighbor Info Module":
"Abilita o disabilita il Modulo Informazioni sui Vicini",
"Enable or disable MQTT": "Abilita o disabilita MQTT",
"Enable Paxcounter": "Abilita Paxcounter",
"Enable Range Test": "Abilita Test di Raggio",
"Enable Serial output": "Abilita output Serial",
"Enable the Air Quality Telemetry":
"Abilita la Telemetria della Qualità dell'Aria",
"Enable the Environment Telemetry": "Abilita la Telemetria Ambientale",
"Enable the Power Telemetry Screen":
"Abilita lo Schermo della Telemetria di Potenza",
"Enable the Power Measurement Telemetry":
"Abilita la Telemetria di Misurazione della Potenza",
"Enable or disable Detection Sensor Module":
"Abilita o disabilita il Modulo Sensore di Rilevamento",
"Enable Smart Position": "Abilita Posizione Intelligente",
"Enable Store & Forward": "Abilita Store & Forward",
"Enable Store & Forward heartbeat":
"Abilita il battito del Store & Forward",
"Enable/Disable transmit (TX) from the LoRa radio":
"Abilita/Disabilita trasmissione (TX) dalla radio LoRa",
Encryption: "Crittografia",
"Encryption Enabled": "Crittografia Abilitata",
"Enable or disable TLS": "Abilita o disabilita TLS",
"Enable or disable MQTT encryption. Note: All messages are sent to the MQTT broker unencrypted if this option is not enabled, even when your uplink channels have encryption keys set. This includes position data.":
"Abilita o disabilita la crittografia MQTT. Nota: Tutti i messaggi vengono inviati al broker MQTT non crittografati se questa opzione non è abilitata, anche quando i tuoi canali di uplink hanno chiavi di crittografia impostate. Questo include i dati di posizione.",
"Enable or disable the Ethernet port":
"Abilita o disabilita la porta Ethernet",
"Enter Message": "Inserisci Messaggio",
"ESP32 Only": "Solo ESP32",
"Ethernet Config": "Configurazione Ethernet",
"Ethernet port configuration": "Configurazione della porta Ethernet",
"Ext Notif": "Notifica Esterna",
"External Notification Settings": "Impostazioni Notifica Esterna",
"Factory Reset Config": "Configurazione Ripristino di Fabbrica",
"Factory Reset Device": "Ripristino di Fabbrica del Dispositivo",
"Flip display 180 degrees": "Ruota il display di 180 gradi",
"Fix north to the top of compass": "Fissa il nord in alto sulla bussola",
"Fixed Position": "Posizione Fissa",
"Flip Screen": "Ribalta Schermo",
"Frequency Slot": "Slot di Frequenza",
"Frequency Offset": "Offset di Frequenza",
"Frequency offset to correct for crystal calibration errors":
"Offset di frequenza per correggere errori di calibrazione del cristallo",
"Friendly Name": "Nome Amichevole",
Gateway: "Gateway",
Generator: "Generatore",
"Generate QR Code": "Genera QR Code",
Goto: "Vai a",
"GPS Display Units": "Unità di Visualizzazione GPS",
"GPS module TX pin override": "Override del pin TX del modulo GPS",
"GPS module enable pin override":
"Override del pin di attivazione del modulo GPS",
"GPIO Pin Value (1-39) For encoder port A":
"Valore Pin GPIO (1-39) per porta encoder A",
"GPIO Pin Value (1-39) For encoder port B":
"Valore Pin GPIO (1-39) per porta encoder B",
"GPIO Pin Value (1-39) For encoder Press":
"Valore Pin GPIO (1-39) per Pressione encoder",
"GPIO pin to use for PTT": "Pin GPIO da utilizzare per PTT",
"GPS Update Interval": "Intervallo di Aggiornamento GPS",
"GPS module RX pin override": "Override del pin RX del modulo GPS",
"GPS Mode": "Modalità GPS",
Green: "Verde",
"Heartbeat Enabled": "Heartbeat Abilitato",
"History return max": "Massimo ritorno della storia",
"History return window": "Finestra di ritorno della storia",
"How fast to cycle through windows":
"Quanto velocemente ciclare tra le finestre",
"Hop Limit": "Limite di Salto",
"How long the device will be in super deep sleep for":
"Quanto tempo il dispositivo sarà in super sonno profondo",
"How long the device will be in light sleep for":
"Quanto tempo il dispositivo sarà in sonno leggero",
"How long to wait between sending test packets":
"Quanto tempo aspettare tra l'invio di pacchetti di test",
"How long to wait between sending paxcounter packets":
"Quanto tempo aspettare tra l'invio di pacchetti paxcounter",
"How often to broadcast node info":
"Quanto spesso trasmettere le informazioni sul nodo",
"How often to send Air Quality data over the mesh":
"Quanto spesso inviare i dati sulla qualità dell'aria sulla rete mesh",
"How often to send Metrics over the mesh":
"Quanto spesso inviare le metriche sulla rete mesh",
"How often to send Power data over the mesh":
"Quanto spesso inviare i dati di potenza sulla rete mesh",
"How often to send position updates":
"Quanto spesso inviare aggiornamenti sulla posizione",
"How often your position is sent out over the mesh":
"Quanto spesso la tua posizione viene inviata sulla rete mesh",
"How often a GPS fix should be acquired":
"Quanto spesso dovrebbe essere acquisito un fix GPS",
"How to handle rebroadcasting": "Come gestire il rebroadcasting",
HTTP: "HTTP",
"i2S WS": "i2S WS",
"GPIO pin to use for i2S WS": "Pin GPIO da utilizzare per i2S WS",
"i2S SD": "i2S SD",
"GPIO pin to use for i2S SD": "Pin GPIO da utilizzare per i2S SD",
"i2S SCK": "i2S SCK",
"GPIO pin to use for i2S SCK": "Pin GPIO da utilizzare per i2S SCK",
"i2S DIN": "i2S DIN",
"GPIO pin to use for i2S DIN": "Pin GPIO da utilizzare per i2S DIN",
"If not sharing precise location, position shared on channel will be accurate within this distance":
"Se non si condivide la posizione precisa, la posizione condivisa sul canale sarà accurata entro questa distanza",
'If true, device is considered to be "managed" by a mesh administrator via admin messages':
"Se vero, le opzioni di configurazione del dispositivo possono essere modificate solo in remoto da un nodo di amministrazione remota tramite messaggi di amministrazione. Non abilitare questa opzione a meno che non sia stato configurato un nodo di amministrazione remota adatto e la chiave pubblica memorizzata nel campo sottostante.",
"If you have a serial port connected to the console, this will override it.":
"Se hai una porta seriale collegata alla console, questo la sovrascriverà.",
"IP Address/Hostname": "Indirizzo IP/Nome Host",
"If the device does not receive a Bluetooth connection, the BLE radio will be disabled after this long":
"Se il dispositivo non riceve una connessione Bluetooth, la radio BLE verrà disabilitata dopo questo tempo",
"Ignore MQTT": "Ignora MQTT",
Import: "Importa",
"Import Channel Set": "Importa Set di Canali",
"Indicates the number of chirps per symbol":
"Indica il numero di beep per simbolo",
Intervals: "Intervalli",
"INA219 Address": "Indirizzo INA219",
"Interval to get telemetry data":
"Intervallo per ottenere dati di telemetria",
"Interval in seconds of how often we should try to send our Neighbor Info to the mesh":
"Intervallo in secondi su quanto spesso dovremmo cercare di inviare le nostre Informazioni sui Vicini alla rete mesh",
"Interval in seconds to publish map reports":
"Intervallo in secondi per pubblicare rapporti sulla mappa",
IP: "IP",
"IP Config": "Configurazione IP",
"IP configuration": "Configurazione IP",
"JSON Enabled": "JSON Abilitato",
"LED State": "Stato LED",
"Legal Information": "Informazioni Legali",
"Last Heard": "Ultimo Contatto",
"Logging Settings": "Impostazioni di Registrazione",
LoRa: "LoRa",
"LoRa frequency channel number": "Numero di canale di frequenza LoRa",
"Light Sleep Duration": "Durata del Sonno Leggero",
"Long Name": "Nome Esteso",
"MAC Address": "Indirizzo MAC",
Manage: "Gestisci",
Managed: "Gestito",
"Manage, connect and disconnect devices":
"Gestisci, collega e disconnetti dispositivi",
Map: "Mappa",
"Map Report Publish Interval (s)":
"Intervallo di Pubblicazione Rapporti Mappa (s)",
"Map Reporting Enabled": "Segnalazione della Mappa Abilitata",
"Max number of records to return": "Numero massimo di record da restituire",
"Max transmit power": "Potenza massima di trasmissione",
"Maximum number of hops": "Numero massimo di salti",
"Mesh Settings": "Impostazioni Mesh",
"Settings for the LoRa mesh": "Impostazioni per la rete LoRa",
Messages: "Messaggi",
"Message Interval": "Intervallo Messaggio",
"Minimum amount of time the device will stay awake for after receiving a packet":
"Tempo minimo che il dispositivo rimarrà attivo dopo aver ricevuto un pacchetto",
"Minimum Broadcast Seconds": "Secondi di Broadcast Minimi",
"Minimum distance (in meters) that must be traveled before a position update is sent":
"Distanza minima (in metri) che deve essere percorsa prima che venga inviato un aggiornamento della posizione",
"Minimum Wake Time": "Tempo Minimo di Risveglio",
"Minimum interval (in seconds) that must pass before a position update is sent":
"Intervallo minimo (in secondi) che deve passare prima che venga inviato un aggiornamento della posizione",
Mode: "Modalità",
Model: "Modello",
"Modem Preset": "Preset Modem",
"Modem preset to use": "Preset Modem da utilizzare",
"Node Info Broadcast Interval":
"Intervallo di Broadcast delle Informazioni sul Nodo",
"Module Enabled": "Modulo Abilitato",
"Monitor Pin": "Pin Monitor",
MQTT: "MQTT",
"MQTT Password": "Password MQTT",
"MQTT password to use for default/custom servers":
"Password MQTT da utilizzare per server predefiniti/custom",
"MQTT root topic to use for default/custom servers":
"Argomento root MQTT da utilizzare per server predefiniti/custom",
"MQTT Server Address": "Indirizzo Server MQTT",
"MQTT server address to use for default/custom servers":
"Indirizzo server MQTT da utilizzare per server predefiniti/custom",
"MQTT Settings": "Impostazioni MQTT",
"MQTT Username": "Nome Utente MQTT",
"MQTT username to use for default/custom servers":
"Nome utente MQTT da utilizzare per server predefiniti/custom",
"Nag Timeout": "Timeout di avviso",
Name: "Nome",
"Neighbor Info": "Informazioni sui vicini",
"Neighbor Info Settings": "Impostazioni Informazioni sui vicini",
network: "Connessione di rete",
"Network name": "Nome della rete",
"Network password": "Password della rete",
Never: "Mai",
"New Connection": "Nuova connessione",
"No Devices": "Nessun dispositivo",
"New device": "Nuovo dispositivo",
"No Connection Bluetooth Disabled":
"Nessuna connessione, Bluetooth disabilitato",
"No Messages": "Nessun messaggio",
"No devices paired yet.": "Nessun dispositivo accoppiato ancora.",
"No Traceroutes": "Nessun traceroute",
Nodes: "Nodi",
"No results found.": "Nessun risultato trovato.",
Now: "Adesso",
"NTP Config": "Configurazione NTP",
"NTP configuration": "Configurazione NTP",
"NTP Server": "Server NTP",
"Number of records": "Numero di registrazioni",
"Number of records to store": "Numero di registrazioni da memorizzare",
"OK to MQTT": "OK a MQTT",
"OLED Type": "Tipo di OLED",
"Only send position when there has been a meaningful change in location":
"Invia posizione solo quando c'è stato un cambiamento significativo nella posizione",
Orange: "Arancione",
Output: "Uscita",
"Output Buzzer": "Uscita Buzzer",
"Output live debug logging over serial, view and export position-redacted device logs over Bluetooth":
"Invio di registrazioni di debug in tempo reale tramite seriale, visualizza ed esporta i log del dispositivo con posizione redatta via Bluetooth",
"Output MS": "Uscita MS",
"Output Vibrate": "Uscita Vibrazione",
"Override Console Serial Port": "Sovrascrivi porta seriale della console",
"Override Duty Cycle": "Sovrascrivi ciclo di lavoro",
"Override Frequency": "Sovrascrivi frequenza",
"Override frequency": "Sovrascrivi frequenza",
"Pairing mode": "Modalità di accoppiamento",
Paxcounter: "Paxcounter",
"Paxcounter Settings": "Impostazioni Paxcounter",
Pin: "PIN",
"Pin selection behaviour.": "Comportamento selezione PIN.",
"Pin to use when pairing": "PIN da utilizzare durante l'accoppiamento",
Pink: "Rosa",
"Please enter a valid bit PSK.":
"Inserisci un PSK valido di {{count}} bit.",
Position: "Posizione",
"Position Flags": "Flag di posizione",
"Position Settings": "Impostazioni posizione",
Power: "Potenza",
"Power Config": "Configurazione potenza",
"Powered by ▲ Vercel": "Alimentato da ▲ Vercel",
"Power Measurement Enabled": "Misurazione della potenza abilitata",
"Power Screen Enabled": "Schermata potenza abilitata",
"Power Update Interval": "Intervallo di aggiornamento potenza",
"Precise Location": "Posizione precisa",
"pre-Shared Key": "Chiave precondivisa",
"Press event": "Evento di pressione",
"Private Key": "Chiave privata",
"Proxy to Client Enabled": "Proxy per client abilitato",
PSK: "PSK",
"PTT Pin": "PIN PTT",
"Public Key": "Chiave pubblica",
Purple: "Viola",
"Query Interval": "Intervallo di query",
"QR Code": "Codice QR",
"Radio Settings": "Impostazioni radio",
"Range Test": "Test di portata",
"Range Test Settings": "Impostazioni test di portata",
"Read more:": "Leggi di più:&nbsp;",
"Reboot the connected node after x minutes.":
"Riavvia il nodo connesso dopo x minuti.",
"Rebroadcast Mode": "Modalità di ripetizione",
"Receive Pin": "PIN di ricezione",
Reconfigure: "Riconfigurare",
Red: "Rosso",
Regenerate: "Rigenera",
"Regenerate Key pair?": "Rigenerare coppia di chiavi?",
Region: "Regione",
Remove: "Rimuovi",
"Remove Node?": "Rimuovere nodo?",
"Replace Channels": "Sostituisci canali",
"Reset Nodes": "Ripristina nodi",
Role: "Ruolo",
"Root topic": "Argomento radice",
"Rotary Encoder #1 Enabled": "Encoder rotativo #1 abilitato",
"Rsyslog Config": "Configurazione Rsyslog",
"Rsyslog configuration": "Configurazione Rsyslog",
"Rsyslog Server": "Server Rsyslog",
"S&F": "S&F",
"Save CSV to storage": "Salva CSV nella memoria",
"Schedule Reboot": "Pianifica riavvio",
"Schedule Shutdown": "Pianifica spegnimento",
"Screen layout variant": "Variante di layout dello schermo",
"Screen Timeout": "Timeout dello schermo",
"Seconds to wait before we consider your packet as 'done'":
"Secondi da attendere prima di considerare il tuo pacchetto come 'completato'",
Security: "Sicurezza",
"Security Settings": "Impostazioni di sicurezza",
"Send Bell": "Invia campanella",
"Sends a bell character with each message":
"Invia un carattere di campanella con ogni messaggio",
"Send ASCII bell with alert message":
"Invia campanella ASCII con messaggio di avviso",
"Select if powered from a low-current source (i.e. solar), to minimize power consumption as much as possible.":
"Seleziona se alimentato da una fonte a bassa corrente (ad es. solare), per ridurre al minimo il consumo energetico il più possibile.",
"Select Mode": "Seleziona modalità",
"Send messages from MQTT to the local mesh":
"Invia messaggi da MQTT alla rete locale",
"Send messages from the local mesh to MQTT":
"Invia messaggi dalla rete locale a MQTT",
"Sent out to other nodes on the mesh to allow them to compute a shared secret key":
"Inviato ad altri nodi sulla rete per consentire loro di calcolare una chiave segreta condivisa",
"Send position to channel": "Invia posizione al canale",
"Send precise location to channel": "Invia posizione precisa al canale",
Serial: "Connessione seriale",
"Serial Console over the Stream API": "Console seriale tramite Stream API",
"Serial Output Enabled": "Uscita seriale abilitata",
"Serial Settings": "Impostazioni seriali",
"Sets the region for your node": "Imposta la regione per il tuo nodo",
"Sets LED to on or off": "Imposta LED su acceso o spento",
"Set the GPIO pin to the RXD pin you have set up.":
"Imposta il pin GPIO sul pin RXD che hai configurato.",
"Settings for the device": "Impostazioni per il dispositivo",
"Settings for the LoRa radio": "Impostazioni per la radio LoRa",
"Settings for Logging": "Impostazioni per la registrazione",
"Settings for the Bluetooth module": "Impostazioni per il modulo Bluetooth",
"Settings for the power module": "Impostazioni per il modulo di potenza",
"Settings for the Serial module": "Impostazioni per il modulo seriale",
"Settings for the Store & Forward module":
"Impostazioni per il modulo Store & Forward",
"Settings for the Telemetry module":
"Impostazioni per il modulo Telemetria",
"Settings for the Range Test module":
"Impostazioni per il modulo Test di portata",
"Settings for the Neighbor Info module":
"Impostazioni per il modulo Informazioni sui vicini",
"Settings for the MQTT module": "Impostazioni per il modulo MQTT",
"Settings for the Canned Message module":
"Impostazioni per il modulo Messaggi predefiniti",
"Settings for the Audio module": "Impostazioni per il modulo Audio",
"Settings for the position module": "Impostazioni per il modulo posizione",
"Show the Telemetry Module on the OLED":
"Mostra il modulo Telemetria sull'OLED",
"Settings for Admin": "Impostazioni per l'Amministratore",
"Settings for the Detection Sensor module":
"Impostazioni per il modulo Sensore di rilevamento",
"Settings for the device display":
"Impostazioni per il display del dispositivo",
"Sharable URL": "URL condivisibile",
"Should an alert be triggered when receiving an incoming bell?":
"Deve essere attivato un avviso alla ricezione di una campanella in arrivo?",
"Smart Position Minimum Distance": "Distanza Minima Posizione Intelligente",
"Smart Position Minimum Interval":
"Intervallo Minimo Posizione Intelligente",
"Short Name": "Nome Breve",
SNR: "SNR",
"Spreading Factor": "Fattore di Spread",
SSID: "SSID",
"State Broadcast Seconds": "Secondi di Trasmissione Stato",
"Select input event.": "Seleziona evento di input.",
"Sets the current for the LED output. Default is 10":
"Imposta la corrente per l'uscita LED. Il valore predefinito è 10",
"Sets the red LED level. Values are 0-255":
"Imposta il livello LED rosso. I valori sono 0-255",
"Sets the green LED level. Values are 0-255":
"Imposta il livello LED verde. I valori sono 0-255",
"Sets the blue LED level. Values are 0-255":
"Imposta il livello LED blu. I valori sono 0-255",
"Settings for the LoRa waveform": "Impostazioni per l'onda LoRa",
"Shutdown on battery delay": "Spegnimento dopo ritardo della batteria",
"Sleep Settings": "Impostazioni di Riposo",
"Sleep settings for the power module":
"Impostazioni di riposo per il modulo di potenza",
"Store & Forward Settings": "Impostazioni Store & Forward",
Subnet: "Sottorete",
"Subnet Mask": "Maschera di Sottorete",
"Super Deep Sleep Duration": "Durata Super Deep Sleep",
"Switch Node": "Nodo di Commutazione",
Telemetry: "Telemetria",
"Telemetry Settings": "Impostazioni Telemetria",
"The Device will restart once the config is saved.":
"Il dispositivo si riavvierà una volta salvata la configurazione.",
"The denominator of the coding rate":
"Il denominatore del tasso di codifica",
"The public key authorized to send admin messages to this node":
"La chiave pubblica autorizzata a inviare messaggi amministrativi a questo nodo",
"The serial baud rate": "La velocità di baud seriale",
"This feature is not implemented yet":
"Questa funzione non è ancora implementata",
"The interval in seconds of how often we can send a message to the mesh when a state change is detected":
"L'intervallo in secondi di quanto spesso possiamo inviare un messaggio alla rete quando viene rilevato un cambiamento di stato",
"The interval in seconds of how often we should send a message to the mesh with the current state regardless of changes":
"L'intervallo in secondi di quanto spesso dovremmo inviare un messaggio alla rete con lo stato attuale, indipendentemente dai cambiamenti",
"The GPIO pin to monitor for state changes":
"Il pin GPIO da monitorare per i cambiamenti di stato",
"The current LoRa configuration will also be shared.":
"La configurazione LoRa attuale sarà condivisa.",
"The current LoRa configuration will be overridden.":
"La configurazione LoRa attuale sarà sovrascritta.",
Timeout: "Timeout",
"TLS Enabled": "TLS Abilitato",
"Toggle Dark Mode": "Attiva Modalità Scura",
"Transmit Enabled": "Trasmissione Abilitata",
"Transmit Pin": "PIN di Trasmissione",
"Transmit Power": "Potenza di Trasmissione",
"Treat double tap as button press":
"Tratta il doppio tocco come pressione del pulsante",
"Turn off the display after this long":
"Spegni il display dopo questo tempo",
"Turn off the connected node after x minutes.":
"Spegni il nodo connesso dopo x minuti.",
"Type of OLED screen attached to the device":
"Tipo di schermo OLED collegato al dispositivo",
"Type a command or search...": "Digita un comando o cerca...",
Unknown: "Sconosciuto",
"Unsupported connection method": "Metodo di connessione non supportato",
"Update Interval": "Intervallo di Aggiornamento",
"Update Interval (seconds)": "Intervallo di Aggiornamento (secondi)",
"Up Down enabled": "Su Giù abilitato",
"Uplink Enabled": "Uplink Abilitato",
"Use I²S Pin as Buzzer": "Usa PIN I²S come Buzzer",
"Use one of the predefined modem presets":
"Usa uno dei preset modem predefiniti",
"Use Preset": "Usa Preset",
"Use Preset?": "Usa Preset?",
"Use Pullup": "Usa Pullup",
"Use PWM": "Usa PWM",
"Use TLS": "Usa TLS",
"Used for tweaking battery voltage reading":
"Utilizzato per modificare la lettura della tensione della batteria",
"Use the client's internet connection for MQTT (feature only active in mobile apps)":
"Usa la connessione Internet del client per MQTT (funzione attiva solo nelle app mobili)",
"Used to create a shared key with a remote device":
"Utilizzato per creare una chiave condivisa con un dispositivo remoto",
"Used to format the message sent to mesh, max 20 Characters":
"Utilizzato per formattare il messaggio inviato alla rete, max 20 caratteri",
"Wake on Tap or Motion": "Attiva al Tocco o Movimento",
"Web Bluetooth and Web Serial are currently only supported by Chromium-based browsers.":
"Web Bluetooth e Web Serial sono attualmente supportati solo dai browser basati su Chromium.",
"WebSerial is currently only supported by Chromium based browsers":
"WebSerial è attualmente supportato solo dai browser basati su Chromium: https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API#browser_compatibility",
"Whether to send/consume JSON packets on MQTT":
"Se inviare/consumare pacchetti JSON su MQTT",
"[WIP] Clear Messages": "[WIP] Cancella Messaggi",
"OK to MQTT description":
"Quando impostato su true, questa configurazione indica che l'utente approva il pacchetto da caricare su MQTT. Se impostato su false, ai nodi remoti viene richiesto di non inoltrare pacchetti a MQTT",
"Wake the device on tap or motion":
"Attiva il dispositivo al tocco o movimento",
"Waveform Settings": "Impostazioni dell'Onda",
"Web Bluetooth": "Web Bluetooth",
"Web Bluetooth is currently only supported by Chromium-based browsers":
"Web Bluetooth è attualmente supportato solo dai browser basati su Chromium",
"Web Serial": "Web Serial",
"What role the device performs on the mesh":
"Quale ruolo svolge il dispositivo sulla rete",
"Whether or not the GPIO pin state detection is triggered on HIGH (1), otherwise LOW (0)":
"Se la rilevazione dello stato del pin GPIO è attivata su HIGH (1), altrimenti LOW (0)",
"Whether or not use INPUT_PULLUP mode for GPIO pin":
"Se utilizzare o meno la modalità INPUT_PULLUP per il pin GPIO",
"WiFi Config": "Configurazione WiFi",
"WiFi radio configuration": "Configurazione radio WiFi",
"Within feet": "Entro {{value}} piedi",
"Within miles": "Entro {{value}} miglia",
"Within m": "Entro {{value}} m",
"Within km": "Entro {{value}} km",
Yellow: "Giallo",
},
};

18
src/pages/Config/DeviceConfig.tsx

@ -6,6 +6,7 @@ import { Network } from "@components/PageComponents/Config/Network.tsx";
import { Position } from "@components/PageComponents/Config/Position.tsx"; import { Position } from "@components/PageComponents/Config/Position.tsx";
import { Power } from "@components/PageComponents/Config/Power.tsx"; import { Power } from "@components/PageComponents/Config/Power.tsx";
import { Security } from "@components/PageComponents/Config/Security.tsx"; import { Security } from "@components/PageComponents/Config/Security.tsx";
import { useTranslation } from "react-i18next";
import { import {
Tabs, Tabs,
TabsContent, TabsContent,
@ -16,40 +17,41 @@ import { useDevice } from "@core/stores/deviceStore.ts";
export const DeviceConfig = (): JSX.Element => { export const DeviceConfig = (): JSX.Element => {
const { metadata } = useDevice(); const { metadata } = useDevice();
const { t } = useTranslation();
const tabs = [ const tabs = [
{ {
label: "Device", label: t("Device"),
element: Device, element: Device,
count: 0, count: 0,
}, },
{ {
label: "Position", label: t("Position"),
element: Position, element: Position,
}, },
{ {
label: "Power", label: t("Power"),
element: Power, element: Power,
}, },
{ {
label: "Network", label: t("network"),
element: Network, element: Network,
// disabled: !metadata.get(0)?.hasWifi, // disabled: !metadata.get(0)?.hasWifi,
}, },
{ {
label: "Display", label: t("Display"),
element: Display, element: Display,
}, },
{ {
label: "LoRa", label: t("LoRa"),
element: LoRa, element: LoRa,
}, },
{ {
label: "Bluetooth", label: t("Bluetooth"),
element: Bluetooth, element: Bluetooth,
}, },
{ {
label: "Security", label: t("Security"),
element: Security, element: Security,
}, },
]; ];

26
src/pages/Config/ModuleConfig.tsx

@ -16,55 +16,57 @@ import {
TabsList, TabsList,
TabsTrigger, TabsTrigger,
} from "@components/UI/Tabs.tsx"; } from "@components/UI/Tabs.tsx";
import { useTranslation } from "react-i18next";
export const ModuleConfig = (): JSX.Element => { export const ModuleConfig = (): JSX.Element => {
const { t } = useTranslation();
const tabs = [ const tabs = [
{ {
label: "MQTT", label: t("MQTT"),
element: MQTT, element: MQTT,
}, },
{ {
label: "Serial", label: t("Serial"),
element: Serial, element: Serial,
}, },
{ {
label: "Ext Notif", label: t("Ext Notif"),
element: ExternalNotification, element: ExternalNotification,
}, },
{ {
label: "S&F", label: t("S&F"),
element: StoreForward, element: StoreForward,
}, },
{ {
label: "Range Test", label: t("Range Test"),
element: RangeTest, element: RangeTest,
}, },
{ {
label: "Telemetry", label: t("Telemetry"),
element: Telemetry, element: Telemetry,
}, },
{ {
label: "Canned", label: t("Canned"),
element: CannedMessage, element: CannedMessage,
}, },
{ {
label: "Audio", label: t("Audio"),
element: Audio, element: Audio,
}, },
{ {
label: "Neighbor Info", label: t("Neighbor Info"),
element: NeighborInfo, element: NeighborInfo,
}, },
{ {
label: "Ambient Lighting", label: t("Ambient Lighting"),
element: AmbientLighting, element: AmbientLighting,
}, },
{ {
label: "Detection Sensor", label: t("Detection Sensor"),
element: DetectionSensor, element: DetectionSensor,
}, },
{ {
label: "Paxcounter", label: t("Paxcounter"),
element: Paxcounter, element: Paxcounter,
}, },
]; ];

14
src/pages/Config/index.tsx

@ -7,6 +7,7 @@ import { useToast } from "@core/hooks/useToast.ts";
import { DeviceConfig } from "@pages/Config/DeviceConfig.tsx"; import { DeviceConfig } from "@pages/Config/DeviceConfig.tsx";
import { ModuleConfig } from "@pages/Config/ModuleConfig.tsx"; import { ModuleConfig } from "@pages/Config/ModuleConfig.tsx";
import { BoxesIcon, SaveIcon, SettingsIcon } from "lucide-react"; import { BoxesIcon, SaveIcon, SettingsIcon } from "lucide-react";
import { useTranslation } from "react-i18next";
import { useState } from "react"; import { useState } from "react";
export const ConfigPage = (): JSX.Element => { export const ConfigPage = (): JSX.Element => {
@ -15,19 +16,20 @@ export const ConfigPage = (): JSX.Element => {
"device" | "module" "device" | "module"
>("device"); >("device");
const { toast } = useToast(); const { toast } = useToast();
const { t } = useTranslation();
return ( return (
<> <>
<Sidebar> <Sidebar>
<SidebarSection label="Config Sections"> <SidebarSection label="Config Sections">
<SidebarButton <SidebarButton
label="Radio Config" label={t("Radio Config")}
active={activeConfigSection === "device"} active={activeConfigSection === "device"}
onClick={() => setActiveConfigSection("device")} onClick={() => setActiveConfigSection("device")}
Icon={SettingsIcon} Icon={SettingsIcon}
/> />
<SidebarButton <SidebarButton
label="Module Config" label={t("Module Config")}
active={activeConfigSection === "module"} active={activeConfigSection === "module"}
onClick={() => setActiveConfigSection("module")} onClick={() => setActiveConfigSection("module")}
Icon={BoxesIcon} Icon={BoxesIcon}
@ -48,8 +50,8 @@ export const ConfigPage = (): JSX.Element => {
await connection?.setConfig(config).then(() => await connection?.setConfig(config).then(() =>
toast({ toast({
title: `Config ${config.payloadVariant.case} saved`, title: `Config ${config.payloadVariant.case} saved`,
}), })
), )
); );
} else { } else {
workingModuleConfig.map( workingModuleConfig.map(
@ -57,8 +59,8 @@ export const ConfigPage = (): JSX.Element => {
await connection?.setModuleConfig(moduleConfig).then(() => await connection?.setModuleConfig(moduleConfig).then(() =>
toast({ toast({
title: `Config ${moduleConfig.payloadVariant.case} saved`, title: `Config ${moduleConfig.payloadVariant.case} saved`,
}), })
), )
); );
} }

18
src/pages/Dashboard/index.tsx

@ -4,6 +4,7 @@ import { Button } from "@components/UI/Button.tsx";
import { Separator } from "@components/UI/Seperator.tsx"; import { Separator } from "@components/UI/Seperator.tsx";
import { H3 } from "@components/UI/Typography/H3.tsx"; import { H3 } from "@components/UI/Typography/H3.tsx";
import { Subtle } from "@components/UI/Typography/Subtle.tsx"; import { Subtle } from "@components/UI/Typography/Subtle.tsx";
import { useTranslation } from "react-i18next";
import { import {
BluetoothIcon, BluetoothIcon,
ListPlusIcon, ListPlusIcon,
@ -17,6 +18,7 @@ import { useMemo } from "react";
export const Dashboard = () => { export const Dashboard = () => {
const { setConnectDialogOpen } = useAppStore(); const { setConnectDialogOpen } = useAppStore();
const { getDevices } = useDeviceStore(); const { getDevices } = useDeviceStore();
const { t } = useTranslation();
const devices = useMemo(() => getDevices(), [getDevices]); const devices = useMemo(() => getDevices(), [getDevices]);
@ -25,8 +27,8 @@ export const Dashboard = () => {
<div className="flex flex-col gap-3 p-3"> <div className="flex flex-col gap-3 p-3">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="space-y-1"> <div className="space-y-1">
<H3>Connected Devices</H3> <H3>{t("Connected Devices")}</H3>
<Subtle>Manage, connect and disconnect devices</Subtle> <Subtle>{t("Manage, connect and disconnect devices")}</Subtle>
</div> </div>
</div> </div>
@ -48,19 +50,19 @@ export const Dashboard = () => {
{device.connection?.connType === "ble" && ( {device.connection?.connType === "ble" && (
<> <>
<BluetoothIcon size={16} /> <BluetoothIcon size={16} />
BLE {"BLE"}
</> </>
)} )}
{device.connection?.connType === "serial" && ( {device.connection?.connType === "serial" && (
<> <>
<UsbIcon size={16} /> <UsbIcon size={16} />
Serial {t("Serial")}
</> </>
)} )}
{device.connection?.connType === "http" && ( {device.connection?.connType === "http" && (
<> <>
<NetworkIcon size={16} /> <NetworkIcon size={16} />
Network {t("Network")}
</> </>
)} )}
</div> </div>
@ -83,14 +85,14 @@ export const Dashboard = () => {
) : ( ) : (
<div className="m-auto flex flex-col gap-3 text-center"> <div className="m-auto flex flex-col gap-3 text-center">
<ListPlusIcon size={48} className="mx-auto text-textSecondary" /> <ListPlusIcon size={48} className="mx-auto text-textSecondary" />
<H3>No Devices</H3> <H3>{t("No Devices")}</H3>
<Subtle>Connect at least one device to get started</Subtle> <Subtle>{t("Connect at least one device to get started")}</Subtle>
<Button <Button
className="gap-2" className="gap-2"
onClick={() => setConnectDialogOpen(true)} onClick={() => setConnectDialogOpen(true)}
> >
<PlusIcon size={16} /> <PlusIcon size={16} />
New Connection {t("New Connection")}
</Button> </Button>
</div> </div>
)} )}

24
src/pages/Nodes.tsx

@ -7,6 +7,7 @@ import { Table } from "@components/generic/Table/index.tsx";
import { TimeAgo } from "@components/generic/Table/tmp/TimeAgo.tsx"; import { TimeAgo } from "@components/generic/Table/tmp/TimeAgo.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { Hashicon } from "@emeraldpay/hashicon-react"; import { Hashicon } from "@emeraldpay/hashicon-react";
import { useTranslation } from "react-i18next";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils"; import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
import { LockIcon, LockOpenIcon, TrashIcon } from "lucide-react"; import { LockIcon, LockOpenIcon, TrashIcon } from "lucide-react";
@ -21,9 +22,10 @@ export interface DeleteNoteDialogProps {
export const NodesPage = (): JSX.Element => { export const NodesPage = (): JSX.Element => {
const { nodes, hardware, setDialogOpen } = useDevice(); const { nodes, hardware, setDialogOpen } = useDevice();
const { setNodeNumToBeRemoved } = useAppStore(); const { setNodeNumToBeRemoved } = useAppStore();
const { t } = useTranslation();
const filteredNodes = Array.from(nodes.values()).filter( const filteredNodes = Array.from(nodes.values()).filter(
(n) => n.num !== hardware.myNodeNum, (n) => n.num !== hardware.myNodeNum
); );
return ( return (
@ -34,14 +36,14 @@ export const NodesPage = (): JSX.Element => {
<Table <Table
headings={[ headings={[
{ title: "", type: "blank", sortable: false }, { title: "", type: "blank", sortable: false },
{ title: "Name", type: "normal", sortable: true }, { title: t("Name"), type: "normal", sortable: true },
{ title: "Model", type: "normal", sortable: true }, { title: t("Model"), type: "normal", sortable: true },
{ title: "MAC Address", type: "normal", sortable: true }, { title: t("MAC Address"), type: "normal", sortable: true },
{ title: "Last Heard", type: "normal", sortable: true }, { title: t("Last Heard"), type: "normal", sortable: true },
{ title: "SNR", type: "normal", sortable: true }, { title: t("SNR"), type: "normal", sortable: true },
{ title: "Encryption", type: "normal", sortable: false }, { title: t("Encryption"), type: "normal", sortable: false },
{ title: "Connection", type: "normal", sortable: true }, { title: t("Connection"), type: "normal", sortable: true },
{ title: "Remove", type: "normal", sortable: false }, { title: t("Remove"), type: "normal", sortable: false },
]} ]}
rows={filteredNodes.map((node) => [ rows={filteredNodes.map((node) => [
<Hashicon key="icon" size={24} value={node.num.toString()} />, <Hashicon key="icon" size={24} value={node.num.toString()} />,
@ -65,7 +67,7 @@ export const NodesPage = (): JSX.Element => {
</Mono>, </Mono>,
<Fragment key="lastHeard"> <Fragment key="lastHeard">
{node.lastHeard === 0 ? ( {node.lastHeard === 0 ? (
<p>Never</p> <p>{t("Never")}</p>
) : ( ) : (
<TimeAgo timestamp={node.lastHeard * 1000} /> <TimeAgo timestamp={node.lastHeard * 1000} />
)} )}
@ -101,7 +103,7 @@ export const NodesPage = (): JSX.Element => {
}} }}
> >
<TrashIcon /> <TrashIcon />
Remove {t("Remove")}
</Button>, </Button>,
])} ])}
/> />

Loading…
Cancel
Save