display new FBOS info

pull/908/head
gabrielburnworth 2018-07-10 19:37:50 -07:00
parent 93ca3609c2
commit c7967182e5
8 changed files with 249 additions and 124 deletions

View File

@ -131,6 +131,35 @@ fieldset {
}
}
.chip-temp-display {
position: relative;
.saucer {
height: 1rem;
width: 1rem;
position: absolute;
right: 1rem;
top: 2px;
cursor: default;
}
}
.wifi-strength-display {
position: relative;
.percent-bar {
width: 25%;
position: absolute;
height: 1rem;
left: 13rem;
top: 2px;
clip-path: polygon(0 85%, 100% 0, 100% 100%, 0% 100%);
background-color: $light_gray;
.percent-bar-fill {
height: 100%;
background-color: $green;
}
}
}
.all-content-wrapper {
margin: 0 auto;
padding: 11rem 3rem 0;

View File

@ -10,10 +10,8 @@ import { FarmbotOsSettings } from "../farmbot_os_settings";
import { mount, shallow } from "enzyme";
import { bot } from "../../../__test_support__/fake_state/bot";
import { fakeResource } from "../../../__test_support__/fake_resource";
import { FbosDetails } from "../fbos_settings/farmbot_os_row";
import { FarmbotOsProps } from "../../interfaces";
import axios from "axios";
import { FbosDetailsProps } from "../fbos_settings/interfaces";
import { Actions } from "../../../constants";
import { SpecialStatus } from "../../../resources/tagged_resources";
@ -81,25 +79,3 @@ describe("<FarmbotOsSettings/>", () => {
});
});
describe("<FbosDetails />", () => {
const fakeProps = (): FbosDetailsProps => {
return {
dispatch: jest.fn(),
bot: bot,
sourceFbosConfig: (x) => {
return { value: bot.hardware.configuration[x], consistent: true };
}
};
};
it("renders", () => {
const wrapper = mount(<FbosDetails {...fakeProps()} />);
["Environment: ---",
"Commit: ---",
"Target: ---",
"Node name: ---",
"Firmware: "].map(string =>
expect(wrapper.text()).toContain(string));
});
});

View File

@ -1,79 +1,30 @@
const mockDevice = {
updateConfig: jest.fn(() => { return Promise.resolve(); }),
};
jest.mock("../../../../device", () => ({
getDevice: () => (mockDevice)
}));
import * as React from "react";
import { FbosDetails } from "../farmbot_os_row";
import { shallow, mount } from "enzyme";
import { FarmbotOsRow } from "../farmbot_os_row";
import { mount } from "enzyme";
import { bot } from "../../../../__test_support__/fake_state/bot";
import { FbosDetailsProps } from "../interfaces";
import { FarmbotOsRowProps } from "../interfaces";
import { fakeState } from "../../../../__test_support__/fake_state";
describe("<FbosDetails/>", () => {
describe("<FarmbotOsRow/>", () => {
beforeEach(function () {
jest.clearAllMocks();
});
const fakeProps = (): FbosDetailsProps => {
const fakeProps = (): FarmbotOsRowProps => {
return {
bot,
osReleaseNotes: "",
dispatch: jest.fn(x => x(jest.fn(), fakeState)),
sourceFbosConfig: (x) => {
return { value: bot.hardware.configuration[x], consistent: true };
}
},
botOnline: false
};
};
it("renders", () => {
bot.hardware.informational_settings.env = "fakeEnv";
bot.hardware.informational_settings.commit = "fakeCommit";
bot.hardware.informational_settings.target = "fakeTarget";
bot.hardware.informational_settings.node_name = "fakeName";
bot.hardware.informational_settings.firmware_version = "fakeFirmware";
bot.hardware.informational_settings.firmware_commit = "fakeFwCommit";
const wrapper = shallow(<FbosDetails {...fakeProps()} />);
["Environment", "fakeEnv",
"Commit", "fakeComm",
"Target", "fakeTarget",
"Node name", "fakeName",
"Firmware", "fakeFirmware",
"Firmware commit", "fakeFwCo",
"Beta release Opt-In"
]
.map(string => expect(wrapper.text()).toContain(string));
});
it("simplifies node name", () => {
const p = fakeProps();
p.bot.hardware.informational_settings.node_name = "name@nodeName";
const wrapper = shallow(<FbosDetails {...p} />);
expect(wrapper.text()).toContain("nodeName");
expect(wrapper.text()).not.toContain("name@");
});
it("toggles os beta opt in setting on", () => {
bot.hardware.configuration.beta_opt_in = false;
const wrapper = mount(<FbosDetails {...fakeProps()} />);
window.confirm = jest.fn();
wrapper.find("button").simulate("click");
expect(window.confirm).toHaveBeenCalledWith(
expect.stringContaining("you sure?"));
expect(mockDevice.updateConfig).not.toHaveBeenCalled();
window.confirm = () => true;
wrapper.find("button").simulate("click");
expect(mockDevice.updateConfig)
.toHaveBeenCalledWith({ beta_opt_in: true });
});
it("toggles os beta opt in setting off", () => {
bot.hardware.configuration.beta_opt_in = true;
const wrapper = mount(<FbosDetails {...fakeProps()} />);
window.confirm = () => false;
wrapper.find("button").simulate("click");
expect(mockDevice.updateConfig)
.toHaveBeenCalledWith({ beta_opt_in: false });
const wrapper = mount(<FarmbotOsRow {...fakeProps()} />);
["FarmBot OS", "Version", "Release Notes"].map(string =>
expect(wrapper.text().toLowerCase()).toContain(string.toLowerCase()));
});
});

View File

@ -0,0 +1,108 @@
const mockDevice = {
updateConfig: jest.fn(() => { return Promise.resolve(); }),
};
jest.mock("../../../../device", () => ({
getDevice: () => (mockDevice)
}));
import * as React from "react";
import { FbosDetails, colorFromTemp } from "../fbos_details";
import { shallow, mount } from "enzyme";
import { bot } from "../../../../__test_support__/fake_state/bot";
import { FbosDetailsProps } from "../interfaces";
import { fakeState } from "../../../../__test_support__/fake_state";
describe("<FbosDetails/>", () => {
beforeEach(function () {
jest.clearAllMocks();
});
const fakeProps = (): FbosDetailsProps => {
return {
botInfoSettings: bot.hardware.informational_settings,
dispatch: jest.fn(x => x(jest.fn(), fakeState)),
sourceFbosConfig: (x) => {
return { value: bot.hardware.configuration[x], consistent: true };
}
};
};
it("renders", () => {
const p = fakeProps();
p.botInfoSettings.env = "fakeEnv";
p.botInfoSettings.commit = "fakeCommit";
p.botInfoSettings.target = "fakeTarget";
p.botInfoSettings.node_name = "fakeName";
p.botInfoSettings.firmware_version = "fakeFirmware";
p.botInfoSettings.firmware_commit = "fakeFwCommit";
p.botInfoSettings.soc_temp = 48.3;
p.botInfoSettings.wifi_level = -49;
const wrapper = mount(<FbosDetails {...p} />);
["Environment", "fakeEnv",
"Commit", "fakeComm",
"Target", "fakeTarget",
"Node name", "fakeName",
"Firmware", "fakeFirmware",
"Firmware commit", "fakeFwCo",
"FAKETARGET CPU temperature", "48.3", "C",
"WiFi Strength", "-49dBm",
"Beta release Opt-In",
]
.map(string => expect(wrapper.text()).toContain(string));
});
it("simplifies node name", () => {
const p = fakeProps();
p.botInfoSettings.node_name = "name@nodeName";
const wrapper = shallow(<FbosDetails {...p} />);
expect(wrapper.text()).toContain("nodeName");
expect(wrapper.text()).not.toContain("name@");
});
it("toggles os beta opt in setting on", () => {
bot.hardware.configuration.beta_opt_in = false;
const wrapper = mount(<FbosDetails {...fakeProps()} />);
window.confirm = jest.fn();
wrapper.find("button").simulate("click");
expect(window.confirm).toHaveBeenCalledWith(
expect.stringContaining("you sure?"));
expect(mockDevice.updateConfig).not.toHaveBeenCalled();
window.confirm = () => true;
wrapper.find("button").simulate("click");
expect(mockDevice.updateConfig)
.toHaveBeenCalledWith({ beta_opt_in: true });
});
it("toggles os beta opt in setting off", () => {
bot.hardware.configuration.beta_opt_in = true;
const wrapper = mount(<FbosDetails {...fakeProps()} />);
window.confirm = () => false;
wrapper.find("button").simulate("click");
expect(mockDevice.updateConfig)
.toHaveBeenCalledWith({ beta_opt_in: false });
});
it("displays N/A when no wifi strength value is undefined", () => {
const p = fakeProps();
p.botInfoSettings.wifi_level = undefined;
const wrapper = mount(<FbosDetails {...p} />);
expect(wrapper.text()).toContain("WiFi Strength: N/A");
expect(wrapper.text()).not.toContain("dBm");
});
});
describe("colorFromTemp()", () => {
it("temperature is good or none", () => {
expect(colorFromTemp(30)).toEqual("green");
expect(colorFromTemp(undefined)).toEqual("gray");
});
it("temperature is hot", () => {
expect(colorFromTemp(61)).toEqual("yellow");
expect(colorFromTemp(76)).toEqual("red");
});
it("temperature is cold", () => {
expect(colorFromTemp(9)).toEqual("blue");
expect(colorFromTemp(-1)).toEqual("lightblue");
});
});

View File

@ -4,39 +4,8 @@ import { t } from "i18next";
import { OsUpdateButton } from "./os_update_button";
import { Popover, Position } from "@blueprintjs/core";
import { ColWidth } from "../farmbot_os_settings";
import { ToggleButton } from "../../../controls/toggle_button";
import { updateConfig } from "../../actions";
import { last } from "lodash";
import { Content } from "../../../constants";
import { FbosDetailsProps, FarmbotOsRowProps } from "./interfaces";
export function FbosDetails(props: FbosDetailsProps) {
const { dispatch, sourceFbosConfig } = props;
const {
env, commit, target, node_name, firmware_version, firmware_commit
} = props.bot.hardware.informational_settings;
const betaOptIn = sourceFbosConfig("beta_opt_in");
const shortenCommit = (longCommit: string) => (longCommit || "").slice(0, 8);
return <div>
<p><b>Environment: </b>{env}</p>
<p><b>Commit: </b>{shortenCommit(commit)}</p>
<p><b>Target: </b>{target}</p>
<p><b>Node name: </b>{last((node_name || "").split("@"))}</p>
<p><b>Firmware: </b>{firmware_version}</p>
<p><b>Firmware commit: </b>{shortenCommit(firmware_commit)}</p>
<fieldset>
<label style={{ marginTop: "0.75rem" }}>
{t("Beta release Opt-In")}
</label>
<ToggleButton
toggleValue={betaOptIn.value}
dim={!betaOptIn.consistent}
toggleAction={() =>
(betaOptIn.value || confirm(Content.OS_BETA_RELEASES)) &&
dispatch(updateConfig({ beta_opt_in: !betaOptIn.value }))} />
</fieldset>
</div>;
}
import { FarmbotOsRowProps } from "./interfaces";
import { FbosDetails } from "./fbos_details";
export function FarmbotOsRow(props: FarmbotOsRowProps) {
const { sourceFbosConfig, dispatch, bot, osReleaseNotes, botOnline } = props;
@ -54,7 +23,7 @@ export function FarmbotOsRow(props: FarmbotOsRowProps) {
{t("Version {{ version }}", { version })}
</p>
<FbosDetails
bot={bot}
botInfoSettings={bot.hardware.informational_settings}
dispatch={dispatch}
sourceFbosConfig={sourceFbosConfig} />
</Popover>

View File

@ -0,0 +1,91 @@
import * as React from "react";
import { Saucer } from "../../../ui/index";
import { t } from "i18next";
import { ToggleButton } from "../../../controls/toggle_button";
import { updateConfig } from "../../actions";
import { last } from "lodash";
import { Content } from "../../../constants";
import { FbosDetailsProps } from "./interfaces";
export const colorFromTemp = (temp: number | undefined): string => {
if (!temp) {
return "gray";
}
if (temp < 0) {
return "lightblue";
} else if (temp < 10) {
return "blue";
} else if (temp > 75) {
return "red";
} else if (temp > 60) {
return "yellow";
} else {
return "green";
}
};
function ChipTemperatureDisplay({ chip, temperature }: {
chip: string, temperature: number | undefined
}): JSX.Element {
return <div className="chip-temp-display">
<p>
<b>{chip.toUpperCase()} {t("CPU temperature")}: </b>
{temperature}&deg;C
</p>
{<Saucer color={colorFromTemp(temperature)} className={"small-inline"} />}
</div>;
}
function WiFiStrengthDisplay({ wifiStrength }: {
wifiStrength: number | undefined
}): JSX.Element {
const percent = wifiStrength
? Math.round(-0.0154 * wifiStrength ** 2 - 0.4 * wifiStrength + 98)
: 0;
const dbString = `${wifiStrength || 0}dBm`;
const percentString = `${percent}%`;
return <div className="wifi-strength-display">
<p>
<b>{t("WiFi Strength")}: </b>
{wifiStrength ? dbString : "N/A"}
</p>
{wifiStrength &&
<div className="percent-bar">
<div
className="percent-bar-fill"
style={{ width: percentString }}
title={dbString} />
</div>}
</div>;
}
export function FbosDetails(props: FbosDetailsProps) {
const { dispatch, sourceFbosConfig, botInfoSettings } = props;
const {
env, commit, target, node_name, firmware_version, firmware_commit,
soc_temp, wifi_level
} = botInfoSettings;
const betaOptIn = sourceFbosConfig("beta_opt_in");
const shortenCommit = (longCommit: string) => (longCommit || "").slice(0, 8);
return <div>
<p><b>Environment: </b>{env}</p>
<p><b>Commit: </b>{shortenCommit(commit)}</p>
<p><b>Target: </b>{target}</p>
<p><b>Node name: </b>{last((node_name || "").split("@"))}</p>
<p><b>Firmware: </b>{firmware_version}</p>
<p><b>Firmware commit: </b>{shortenCommit(firmware_commit)}</p>
<ChipTemperatureDisplay chip={target} temperature={soc_temp} />
<WiFiStrengthDisplay wifiStrength={wifi_level} />
<fieldset>
<label style={{ marginTop: "0.75rem" }}>
{t("Beta release Opt-In")}
</label>
<ToggleButton
toggleValue={betaOptIn.value}
dim={!betaOptIn.consistent}
toggleAction={() =>
(betaOptIn.value || confirm(Content.OS_BETA_RELEASES)) &&
dispatch(updateConfig({ beta_opt_in: !betaOptIn.value }))} />
</fieldset>
</div>;
}

View File

@ -1,7 +1,7 @@
import {
SourceFbosConfig, BotState, ControlPanelState, ShouldDisplay
} from "../../interfaces";
import { Dictionary } from "farmbot";
import { Dictionary, InformationalSettings } from "farmbot";
export interface AutoSyncRowProps {
dispatch: Function;
@ -52,7 +52,7 @@ export interface FarmbotOsRowProps {
}
export interface FbosDetailsProps {
bot: BotState;
botInfoSettings: InformationalSettings;
dispatch: Function;
sourceFbosConfig: SourceFbosConfig;
}

View File

@ -3,11 +3,12 @@ import * as React from "react";
export interface SaucerProps {
color?: string;
active?: boolean;
className?: string;
}
/** A colored UI disc/circle. */
export function Saucer({ color, active }: SaucerProps) {
let className = `saucer ${color}`;
if (active) { className += " active"; }
return <div className={className} />;
export function Saucer({ color, active, className }: SaucerProps) {
const classes = ["saucer", color, className];
if (active) { classes.push("active"); }
return <div className={classes.join(" ")} />;
}