display new FBOS info
parent
93ca3609c2
commit
c7967182e5
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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()));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
});
|
|
@ -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>
|
||||
|
|
|
@ -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}°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>;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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(" ")} />;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue