update fbos details

pull/1282/head
gabrielburnworth 2019-07-16 12:07:29 -07:00
parent 522faac3f1
commit 1fb8048ba4
7 changed files with 124 additions and 45 deletions

View File

@ -187,6 +187,15 @@ fieldset {
}
}
.throttle-display {
.throttle-row {
display: flex;
.saucer {
margin-right: 1rem;
}
}
}
.wifi-strength-display {
position: relative;
.percent-bar {

View File

@ -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"));
});
});

View File

@ -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 {

View File

@ -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 <div className="chip-temp-display">
<p>
<b>{chip && chip.toUpperCase()} {t("CPU temperature")}: </b>
@ -41,15 +47,20 @@ export function ChipTemperatureDisplay({ chip, temperature }: {
</div>;
}
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 <div className="wifi-strength-display">
<p>
<b>{t("WiFi Strength")}: </b>
@ -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 <Saucer className={"small-inline"}
title={THROTTLE_COLOR_KEY()[throttleColor]}
color={throttleColor} />;
};
/** Visual representation of throttle state. */
const ThrottleDisplay = (dataString: string) =>
<div className="throttle-display">
{Object.keys(THROTTLE_BIT_LOOKUP).map((key: ThrottleType) =>
<div className="throttle-row" key={key}>
<ThrottleIndicator throttleDataString={dataString} throttleType={key} />
<p>{key}</p>
</div>)}
</div>;
interface VoltageDisplayProps {
chip?: string;
throttled: string | undefined;
@ -112,17 +153,27 @@ export const VoltageDisplay = ({ chip, throttled }: VoltageDisplayProps) =>
<p>
<b>{chip && chip.toUpperCase()} {t("Voltage")}: </b>
</p>
<Saucer className={"small-inline"}
color={colorFromThrottle(throttled, ThrottleType.UnderVoltage)} />
<Popover usePortal={false}>
<ThrottleIndicator
throttleDataString={throttled}
throttleType={ThrottleType.UnderVoltage} />
{ThrottleDisplay(throttled)}
</Popover>
</div> : <div className="voltage-display" />;
/** 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 <p>
<b>{title}: </b>
@ -136,8 +187,12 @@ const CommitDisplay = ({ title, repo, commit }: {
</p>;
};
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 <p><b>{t("Uptime")}: </b>{convertUptime(uptime_sec)}</p>;
};
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 <fieldset>
<label style={{ marginTop: "0.75rem" }}>
{t("Beta release Opt-In")}
</label>
<ToggleButton
toggleValue={betaOptInValue}
dim={!betaOptIn.consistent}
toggleAction={() =>
(betaOptInValue || confirm(Content.OS_BETA_RELEASES)) &&
dispatch(updateConfig(update))} />
</fieldset>;
};
const BetaReleaseOptInButton = (
{ dispatch, sourceFbosConfig, shouldDisplay }: BetaReleaseOptInButtonProps
): JSX.Element => {
const { betaOptIn, betaOptInValue, update } =
betaReleaseOptIn({ sourceFbosConfig, shouldDisplay });
return <fieldset>
<label style={{ marginTop: "0.75rem" }}>
{t("Beta release Opt-In")}
</label>
<ToggleButton
toggleValue={betaOptInValue}
dim={!betaOptIn.consistent}
toggleAction={() =>
(betaOptInValue || confirm(Content.OS_BETA_RELEASES)) &&
dispatch(updateConfig(update))} />
</fieldset>;
};
/** 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 <div>
<LastSeen
@ -219,7 +285,8 @@ export function FbosDetails(props: FbosDetailsProps) {
<p><b>{t("Memory usage")}: </b>{memory_usage}MB</p>}
{isNumber(disk_usage) && <p><b>{t("Disk usage")}: </b>{disk_usage}%</p>}
<ChipTemperatureDisplay chip={target} temperature={soc_temp} />
<WiFiStrengthDisplay wifiStrength={wifi_level} />
<WiFiStrengthDisplay
wifiStrength={wifi_level} wifiStrengthPercent={wifi_level_percent} />
<VoltageDisplay chip={target} throttled={throttled} />
<BetaReleaseOptInButton
dispatch={props.dispatch}

View File

@ -17,7 +17,7 @@ describe("Contextual `Alert` creation", () => {
expect(results[0]).toEqual({
created_at: 1,
problem_tag: "farmbot_os.firmware.missing",
priority: 0,
priority: 500,
slug: "firmware-missing",
});
});

View File

@ -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",
};
}

View File

@ -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 <div className={classes.join(" ")} />;
return <div className={classes.join(" ")} title={title} />;
}