diff --git a/frontend/css/global.scss b/frontend/css/global.scss index a931b08d9..d9bd52f18 100644 --- a/frontend/css/global.scss +++ b/frontend/css/global.scss @@ -187,6 +187,15 @@ fieldset { } } +.throttle-display { + .throttle-row { + display: flex; + .saucer { + margin-right: 1rem; + } + } +} + .wifi-strength-display { position: relative; .percent-bar { diff --git a/frontend/devices/__tests__/actions_test.ts b/frontend/devices/__tests__/actions_test.ts index 4a8431a91..0799d4271 100644 --- a/frontend/devices/__tests__/actions_test.ts +++ b/frontend/devices/__tests__/actions_test.ts @@ -264,8 +264,11 @@ describe("isLog()", function () { it("filters sensitive logs", () => { const log = { message: "NERVESPSKWPASSWORD" }; + console.error = jest.fn(); const result = actions.isLog(log); expect(result).toBe(false); + expect(console.error).toHaveBeenCalledWith( + expect.stringContaining("Refusing to display log")); }); }); diff --git a/frontend/devices/actions.ts b/frontend/devices/actions.ts index b315d2558..2668b4821 100644 --- a/frontend/devices/actions.ts +++ b/frontend/devices/actions.ts @@ -33,8 +33,7 @@ export const FEATURE_MIN_VERSIONS_URL = "https://raw.githubusercontent.com/FarmBot/farmbot_os/staging/" + "FEATURE_MIN_VERSIONS.json"; // Already filtering messages in FarmBot OS and the API- this is just for -// an additional layer of safety. If sensitive data ever hits a client, it will -// be reported to Rollbar for investigation. +// an additional layer of safety. const BAD_WORDS = ["WPA", "PSK", "PASSWORD", "NERVES"]; export function isLog(x: unknown): x is Log { diff --git a/frontend/devices/components/fbos_settings/fbos_details.tsx b/frontend/devices/components/fbos_settings/fbos_details.tsx index a3e5e4067..05ff9a07f 100644 --- a/frontend/devices/components/fbos_settings/fbos_details.tsx +++ b/frontend/devices/components/fbos_settings/fbos_details.tsx @@ -9,6 +9,7 @@ import { SourceFbosConfig, ShouldDisplay, Feature } from "../../interfaces"; import { ConfigurationName } from "farmbot"; import { t } from "../../../i18next_wrapper"; import { LastSeen } from "./last_seen_row"; +import { Popover } from "@blueprintjs/core"; /** Return an indicator color for the given temperature (C). */ export const colorFromTemp = (temp: number | undefined): string => { @@ -28,10 +29,15 @@ export const colorFromTemp = (temp: number | undefined): string => { } }; +interface ChipTemperatureDisplayProps { + chip?: string; + temperature: number | undefined; +} + /** RPI CPU temperature display row: label, temperature, indicator. */ -export function ChipTemperatureDisplay({ chip, temperature }: { - chip?: string, temperature: number | undefined -}): JSX.Element { +export function ChipTemperatureDisplay( + { chip, temperature }: ChipTemperatureDisplayProps +): JSX.Element { return

{chip && chip.toUpperCase()} {t("CPU temperature")}: @@ -41,15 +47,20 @@ export function ChipTemperatureDisplay({ chip, temperature }: {

; } +interface WiFiStrengthDisplayProps { + wifiStrength: number | undefined; + wifiStrengthPercent?: number | undefined; +} + /** WiFi signal strength display row: label, strength, indicator. */ -export function WiFiStrengthDisplay({ wifiStrength }: { - wifiStrength: number | undefined -}): JSX.Element { +export function WiFiStrengthDisplay( + { wifiStrength, wifiStrengthPercent }: WiFiStrengthDisplayProps +): JSX.Element { const percent = wifiStrength ? Math.round(-0.0154 * wifiStrength ** 2 - 0.4 * wifiStrength + 98) : 0; const dbString = `${wifiStrength || 0}dBm`; - const percentString = `${percent}%`; + const percentString = `${wifiStrengthPercent || percent}%`; return

{t("WiFi Strength")}: @@ -100,6 +111,36 @@ export const colorFromThrottle = } }; +const THROTTLE_COLOR_KEY = () => ({ + red: t("active"), + yellow: t("occurred"), + green: t("clear") +}); + +interface ThrottleIndicatorProps { + throttleDataString: string; + throttleType: ThrottleType; +} + +/** Saucer with color and title indicating throttle state. */ +const ThrottleIndicator = (props: ThrottleIndicatorProps) => { + const { throttleDataString, throttleType } = props; + const throttleColor = colorFromThrottle(throttleDataString, throttleType); + return ; +}; + +/** Visual representation of throttle state. */ +const ThrottleDisplay = (dataString: string) => +

+ {Object.keys(THROTTLE_BIT_LOOKUP).map((key: ThrottleType) => +
+ +

{key}

+
)} +
; + interface VoltageDisplayProps { chip?: string; throttled: string | undefined; @@ -112,17 +153,27 @@ export const VoltageDisplay = ({ chip, throttled }: VoltageDisplayProps) =>

{chip && chip.toUpperCase()} {t("Voltage")}:

- + + + {ThrottleDisplay(throttled)} +
:
; /** Get the first 8 characters of a commit. */ const shortenCommit = (longCommit: string) => (longCommit || "").slice(0, 8); +interface CommitDisplayProps { + title: string; + repo: string; + commit: string; +} + /** GitHub commit display row: label, commit link. */ -const CommitDisplay = ({ title, repo, commit }: { - title: string, repo: string, commit: string -}): JSX.Element => { +const CommitDisplay = ( + { title, repo, commit }: CommitDisplayProps +): JSX.Element => { const shortCommit = shortenCommit(commit); return

{title}: @@ -136,8 +187,12 @@ const CommitDisplay = ({ title, repo, commit }: {

; }; +interface UptimeDisplayProps { + uptime_sec: number; +} + /** FBOS uptime display row: label and uptime in relevant unit. */ -const UptimeDisplay = ({ uptime_sec }: { uptime_sec: number }): JSX.Element => { +const UptimeDisplay = ({ uptime_sec }: UptimeDisplayProps): JSX.Element => { const convertUptime = (seconds: number) => { if (seconds >= 172800) { return `${Math.round(seconds / 86400)} ${t("days")}`; @@ -152,9 +207,15 @@ const UptimeDisplay = ({ uptime_sec }: { uptime_sec: number }): JSX.Element => { return

{t("Uptime")}: {convertUptime(uptime_sec)}

; }; -export const betaReleaseOptIn = ({ sourceFbosConfig, shouldDisplay }: { - sourceFbosConfig: SourceFbosConfig, shouldDisplay: ShouldDisplay -}) => { +interface BetaReleaseOptInParams { + sourceFbosConfig: SourceFbosConfig; + shouldDisplay: ShouldDisplay; +} + +/** Generate params for BetaReleaseOptInButton. */ +export const betaReleaseOptIn = ( + { sourceFbosConfig, shouldDisplay }: BetaReleaseOptInParams +) => { if (shouldDisplay(Feature.use_update_channel)) { const betaOptIn = sourceFbosConfig("update_channel" as ConfigurationName); const betaOptInValue = betaOptIn.value !== "stable"; @@ -172,34 +233,39 @@ export const betaReleaseOptIn = ({ sourceFbosConfig, shouldDisplay }: { } }; +interface BetaReleaseOptInButtonProps { + dispatch: Function; + sourceFbosConfig: SourceFbosConfig; + shouldDisplay: ShouldDisplay; +} + /** Label and toggle button for opting in to FBOS beta releases. */ -const BetaReleaseOptInButton = - ({ dispatch, sourceFbosConfig, shouldDisplay }: { - dispatch: Function, - sourceFbosConfig: SourceFbosConfig, - shouldDisplay: ShouldDisplay, - }): JSX.Element => { - const { betaOptIn, betaOptInValue, update } = - betaReleaseOptIn({ sourceFbosConfig, shouldDisplay }); - return
- - - (betaOptInValue || confirm(Content.OS_BETA_RELEASES)) && - dispatch(updateConfig(update))} /> -
; - }; +const BetaReleaseOptInButton = ( + { dispatch, sourceFbosConfig, shouldDisplay }: BetaReleaseOptInButtonProps +): JSX.Element => { + const { betaOptIn, betaOptInValue, update } = + betaReleaseOptIn({ sourceFbosConfig, shouldDisplay }); + return
+ + + (betaOptInValue || confirm(Content.OS_BETA_RELEASES)) && + dispatch(updateConfig(update))} /> +
; +}; /** Current technical information about FarmBot OS running on the device. */ export function FbosDetails(props: FbosDetailsProps) { const { env, commit, target, node_name, firmware_version, firmware_commit, - soc_temp, wifi_level, uptime, memory_usage, disk_usage, throttled - } = props.botInfoSettings; + soc_temp, wifi_level, uptime, memory_usage, disk_usage, throttled, + wifi_level_percent, + // tslint:disable-next-line:no-any + } = props.botInfoSettings as any; return
{t("Memory usage")}: {memory_usage}MB

} {isNumber(disk_usage) &&

{t("Disk usage")}: {disk_usage}%

} - + { expect(results[0]).toEqual({ created_at: 1, problem_tag: "farmbot_os.firmware.missing", - priority: 0, + priority: 500, slug: "firmware-missing", }); }); diff --git a/frontend/messages/reducer.ts b/frontend/messages/reducer.ts index 41f937ece..d912ebb42 100644 --- a/frontend/messages/reducer.ts +++ b/frontend/messages/reducer.ts @@ -23,7 +23,7 @@ const toggleAlert = (s: State, body: TaggedFbosConfig["body"]) => { s.alerts[FIRMWARE_MISSING] = { created_at: 1, problem_tag: FIRMWARE_MISSING, - priority: 0, + priority: 500, slug: "firmware-missing", }; } diff --git a/frontend/ui/saucer.tsx b/frontend/ui/saucer.tsx index 1686bfc03..ea2e2001c 100644 --- a/frontend/ui/saucer.tsx +++ b/frontend/ui/saucer.tsx @@ -4,11 +4,12 @@ export interface SaucerProps { color?: string; active?: boolean; className?: string; + title?: string; } /** A colored UI disc/circle. */ -export function Saucer({ color, active, className }: SaucerProps) { +export function Saucer({ color, active, className, title }: SaucerProps) { const classes = ["saucer", color, className]; if (active) { classes.push("active"); } - return
; + return
; }