Skip to content

Commit

Permalink
update fbos details
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielburnworth committed Jul 16, 2019
1 parent 522faac commit 1fb8048
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 45 deletions.
9 changes: 9 additions & 0 deletions frontend/css/global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,15 @@ fieldset {
}
}

.throttle-display {
.throttle-row {
display: flex;
.saucer {
margin-right: 1rem;
}
}
}

.wifi-strength-display {
position: relative;
.percent-bar {
Expand Down
3 changes: 3 additions & 0 deletions frontend/devices/__tests__/actions_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
});
});

Expand Down
3 changes: 1 addition & 2 deletions frontend/devices/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
145 changes: 106 additions & 39 deletions frontend/devices/components/fbos_settings/fbos_details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand All @@ -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>
Expand All @@ -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>
Expand Down Expand Up @@ -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;
Expand All @@ -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>
Expand All @@ -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")}`;
Expand All @@ -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";
Expand All @@ -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
Expand All @@ -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}
Expand Down
2 changes: 1 addition & 1 deletion frontend/messages/__tests__/reducer_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
});
});
Expand Down
2 changes: 1 addition & 1 deletion frontend/messages/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
};
}
Expand Down
5 changes: 3 additions & 2 deletions frontend/ui/saucer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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} />;
}

0 comments on commit 1fb8048

Please sign in to comment.