commit
fbdebb1b76
|
@ -129,7 +129,7 @@ const DataPoints = ({ plotProps, parentProps }: {
|
||||||
plotProps: PlotProps, parentProps: SensorReadingPlotProps
|
plotProps: PlotProps, parentProps: SensorReadingPlotProps
|
||||||
}) =>
|
}) =>
|
||||||
<g id="sensor-readings">
|
<g id="sensor-readings">
|
||||||
{["current", "previous"].map((period: "current" | "previous") =>
|
{["previous", "current"].map((period: "current" | "previous") =>
|
||||||
<g id={period} key={period}>
|
<g id={period} key={period}>
|
||||||
{parentProps.readingsForPeriod(period).map(r => {
|
{parentProps.readingsForPeriod(period).map(r => {
|
||||||
const created_at =
|
const created_at =
|
||||||
|
|
|
@ -90,18 +90,19 @@ export class SensorReadingsTable
|
||||||
<table className="sensor-history-table-contents">
|
<table className="sensor-history-table-contents">
|
||||||
<tbody>
|
<tbody>
|
||||||
{["current", "previous"].map((period: "current" | "previous") => {
|
{["current", "previous"].map((period: "current" | "previous") => {
|
||||||
return this.props.readingsForPeriod(period).map(sensorReading => {
|
return this.props.readingsForPeriod(period).reverse()
|
||||||
const pin = sensorReading.body.pin;
|
.map(sensorReading => {
|
||||||
const sensorName = `${sensorNameByPinLookup[pin]} (pin ${pin})`;
|
const pin = sensorReading.body.pin;
|
||||||
return <TableRow
|
const sensorName = `${sensorNameByPinLookup[pin]} (pin ${pin})`;
|
||||||
key={sensorReading.uuid}
|
return <TableRow
|
||||||
sensorName={sensorName}
|
key={sensorReading.uuid}
|
||||||
sensorReading={sensorReading}
|
sensorName={sensorName}
|
||||||
timeOffset={this.props.timeOffset}
|
sensorReading={sensorReading}
|
||||||
period={period}
|
timeOffset={this.props.timeOffset}
|
||||||
hover={this.props.hover}
|
period={period}
|
||||||
hovered={this.props.hovered} />;
|
hover={this.props.hover}
|
||||||
});
|
hovered={this.props.hovered} />;
|
||||||
|
});
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -280,7 +280,7 @@ a {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
ul {
|
ul {
|
||||||
font-size: 0.3rem;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,15 +31,16 @@ describe("<OsUpdateButton/>", () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
it("renders buttons: not connected", () => {
|
it("renders buttons: not connected", () => {
|
||||||
|
bot.hardware.informational_settings.controller_version = undefined;
|
||||||
bot.currentOSVersion = undefined;
|
bot.currentOSVersion = undefined;
|
||||||
const buttons = mount(<OsUpdateButton {...fakeProps()} />);
|
const buttons = mount(<OsUpdateButton {...fakeProps()} />);
|
||||||
expect(buttons.find("button").length).toBe(1);
|
expect(buttons.find("button").length).toBe(1);
|
||||||
const autoUpdate = buttons.find("button").first();
|
const autoUpdate = buttons.find("button").first();
|
||||||
expect(autoUpdate.hasClass("yellow")).toBeTruthy();
|
expect(autoUpdate.hasClass("yellow")).toBeTruthy();
|
||||||
const osUpdateButton = buttons.find("button").last();
|
const osUpdateButton = buttons.find("button").last();
|
||||||
expect(osUpdateButton.text()).toBe("Can't connect to release server");
|
expect(osUpdateButton.text()).toBe("Can't connect to bot");
|
||||||
});
|
});
|
||||||
it("renders buttons: not connected to bot", () => {
|
it("renders buttons: connected to releases but not bot", () => {
|
||||||
bot.hardware.informational_settings.controller_version = undefined;
|
bot.hardware.informational_settings.controller_version = undefined;
|
||||||
const buttons = mount(<OsUpdateButton {...fakeProps()} />);
|
const buttons = mount(<OsUpdateButton {...fakeProps()} />);
|
||||||
expect(buttons.find("button").length).toBe(1);
|
expect(buttons.find("button").length).toBe(1);
|
||||||
|
@ -48,14 +49,30 @@ describe("<OsUpdateButton/>", () => {
|
||||||
const osUpdateButton = buttons.find("button").last();
|
const osUpdateButton = buttons.find("button").last();
|
||||||
expect(osUpdateButton.text()).toBe("Can't connect to bot");
|
expect(osUpdateButton.text()).toBe("Can't connect to bot");
|
||||||
});
|
});
|
||||||
it("renders buttons: no beta releases", () => {
|
it("renders buttons: no releases available", () => {
|
||||||
|
bot.hardware.informational_settings.controller_version = "3.1.6";
|
||||||
bot.hardware.configuration.beta_opt_in = true;
|
bot.hardware.configuration.beta_opt_in = true;
|
||||||
|
bot.currentOSVersion = undefined;
|
||||||
const buttons = mount(<OsUpdateButton {...fakeProps()} />);
|
const buttons = mount(<OsUpdateButton {...fakeProps()} />);
|
||||||
expect(buttons.find("button").length).toBe(1);
|
|
||||||
const autoUpdate = buttons.find("button").first();
|
|
||||||
expect(autoUpdate.hasClass("yellow")).toBeTruthy();
|
|
||||||
const osUpdateButton = buttons.find("button").last();
|
const osUpdateButton = buttons.find("button").last();
|
||||||
expect(osUpdateButton.text()).toBe("No beta releases available");
|
expect(osUpdateButton.text()).toBe("Can't connect to release server");
|
||||||
|
});
|
||||||
|
it("renders buttons: only beta release available", () => {
|
||||||
|
bot.hardware.informational_settings.controller_version = "3.1.6";
|
||||||
|
bot.hardware.configuration.beta_opt_in = true;
|
||||||
|
bot.currentOSVersion = undefined;
|
||||||
|
bot.currentBetaOSVersion = "3.1.7";
|
||||||
|
const buttons = mount(<OsUpdateButton {...fakeProps()} />);
|
||||||
|
const osUpdateButton = buttons.find("button").last();
|
||||||
|
expect(osUpdateButton.text()).toBe("UPDATE");
|
||||||
|
});
|
||||||
|
it("renders buttons: no beta release available", () => {
|
||||||
|
bot.hardware.informational_settings.controller_version = "3.1.6";
|
||||||
|
bot.hardware.configuration.beta_opt_in = true;
|
||||||
|
bot.currentBetaOSVersion = undefined;
|
||||||
|
const buttons = mount(<OsUpdateButton {...fakeProps()} />);
|
||||||
|
const osUpdateButton = buttons.find("button").last();
|
||||||
|
expect(osUpdateButton.text()).toBe("UP TO DATE");
|
||||||
});
|
});
|
||||||
it("up to date", () => {
|
it("up to date", () => {
|
||||||
bot.hardware.informational_settings.controller_version = "3.1.6";
|
bot.hardware.informational_settings.controller_version = "3.1.6";
|
||||||
|
@ -87,6 +104,25 @@ describe("<OsUpdateButton/>", () => {
|
||||||
expect(osUpdateButton.text()).toBe("UPDATE");
|
expect(osUpdateButton.text()).toBe("UPDATE");
|
||||||
expect(osUpdateButton.props().title).toBe("5.0.0-beta");
|
expect(osUpdateButton.props().title).toBe("5.0.0-beta");
|
||||||
});
|
});
|
||||||
|
it("latest newer than beta update: latest installed", () => {
|
||||||
|
bot.hardware.informational_settings.controller_version = "3.1.6";
|
||||||
|
bot.hardware.configuration.beta_opt_in = true;
|
||||||
|
bot.currentBetaOSVersion = "3.1.4-beta";
|
||||||
|
const buttons = mount(<OsUpdateButton {...fakeProps()} />);
|
||||||
|
const osUpdateButton = buttons.find("button").last();
|
||||||
|
expect(osUpdateButton.text()).toBe("UP TO DATE");
|
||||||
|
expect(osUpdateButton.props().title).toBe("3.1.6");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("latest newer than beta update: beta installed", () => {
|
||||||
|
bot.hardware.informational_settings.controller_version = "3.1.5";
|
||||||
|
bot.hardware.configuration.beta_opt_in = true;
|
||||||
|
bot.currentBetaOSVersion = "3.1.5-beta";
|
||||||
|
const buttons = mount(<OsUpdateButton {...fakeProps()} />);
|
||||||
|
const osUpdateButton = buttons.find("button").last();
|
||||||
|
expect(osUpdateButton.text()).toBe("UPDATE");
|
||||||
|
expect(osUpdateButton.props().title).toBe("3.1.6");
|
||||||
|
});
|
||||||
it("beta update has same numeric version: newer commit", () => {
|
it("beta update has same numeric version: newer commit", () => {
|
||||||
bot.hardware.informational_settings.controller_version = "5.0.0";
|
bot.hardware.informational_settings.controller_version = "5.0.0";
|
||||||
bot.hardware.informational_settings.commit = "old commit";
|
bot.hardware.informational_settings.commit = "old commit";
|
||||||
|
|
|
@ -1,21 +1,23 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import * as _ from "lodash";
|
|
||||||
import { JobProgress } from "farmbot/dist";
|
import { JobProgress } from "farmbot/dist";
|
||||||
import { SemverResult, semverCompare } from "../../../util";
|
import { SemverResult, semverCompare } from "../../../util";
|
||||||
import { OsUpdateButtonProps } from "./interfaces";
|
import { OsUpdateButtonProps } from "./interfaces";
|
||||||
import { checkControllerUpdates } from "../../actions";
|
import { checkControllerUpdates } from "../../actions";
|
||||||
|
import { isString } from "lodash";
|
||||||
|
|
||||||
enum UpdateButton { upToDate, needsUpdate, unknown, unknownBeta, none }
|
/** FBOS update button states. */
|
||||||
|
enum UpdateButton { upToDate, needsUpdate, unknown, none }
|
||||||
|
|
||||||
const buttonProps = (status: UpdateButton) => {
|
/** FBOS update button state => props. */
|
||||||
|
const buttonProps = (status: UpdateButton): {
|
||||||
|
color: "green" | "gray" | "yellow", text: string
|
||||||
|
} => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case UpdateButton.needsUpdate:
|
case UpdateButton.needsUpdate:
|
||||||
return { color: "green", text: t("UPDATE") };
|
return { color: "green", text: t("UPDATE") };
|
||||||
case UpdateButton.upToDate:
|
case UpdateButton.upToDate:
|
||||||
return { color: "gray", text: t("UP TO DATE") };
|
return { color: "gray", text: t("UP TO DATE") };
|
||||||
case UpdateButton.unknownBeta:
|
|
||||||
return { color: "yellow", text: t("No beta releases available") };
|
|
||||||
case UpdateButton.unknown:
|
case UpdateButton.unknown:
|
||||||
return { color: "yellow", text: t("Can't connect to release server") };
|
return { color: "yellow", text: t("Can't connect to release server") };
|
||||||
default:
|
default:
|
||||||
|
@ -23,68 +25,99 @@ const buttonProps = (status: UpdateButton) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export let OsUpdateButton = (props: OsUpdateButtonProps) => {
|
/** FBOS update download in progress. */
|
||||||
|
const isWorking = (job: JobProgress | undefined) =>
|
||||||
|
job && (job.status == "working");
|
||||||
|
|
||||||
|
/** FBOS update download progress. */
|
||||||
|
function downloadProgress(job: JobProgress | undefined) {
|
||||||
|
if (job && isWorking(job)) {
|
||||||
|
switch (job.unit) {
|
||||||
|
case "bytes":
|
||||||
|
const kiloBytes = Math.round(job.bytes / 1024);
|
||||||
|
const megaBytes = Math.round(job.bytes / 1048576);
|
||||||
|
if (kiloBytes < 1) {
|
||||||
|
return job.bytes + "B";
|
||||||
|
} else if (megaBytes < 1) {
|
||||||
|
return kiloBytes + "kB";
|
||||||
|
} else {
|
||||||
|
return megaBytes + "MB";
|
||||||
|
}
|
||||||
|
case "percent":
|
||||||
|
return job.percent + "%";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Determine the latest available version. */
|
||||||
|
const getLatestVersion = (
|
||||||
|
currentOSVersion: string | undefined,
|
||||||
|
currentBetaOSVersion: string | undefined,
|
||||||
|
betaOptIn: boolean
|
||||||
|
): string | undefined => {
|
||||||
|
if (!betaOptIn) { return currentOSVersion; }
|
||||||
|
switch (semverCompare(currentOSVersion || "", currentBetaOSVersion || "")) {
|
||||||
|
case SemverResult.RIGHT_IS_GREATER: return currentBetaOSVersion;
|
||||||
|
default: return currentOSVersion;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Unequal beta commits => needs update. */
|
||||||
|
const betaCommitsAreEqual = (
|
||||||
|
fbosCommit: string | undefined,
|
||||||
|
currentBetaOSCommit: string | undefined): boolean =>
|
||||||
|
!(isString(fbosCommit) && isString(currentBetaOSCommit)
|
||||||
|
&& fbosCommit !== currentBetaOSCommit);
|
||||||
|
|
||||||
|
/** Determine the FBOS update button state. */
|
||||||
|
const compareWithBotVersion = (
|
||||||
|
candidate: string | undefined,
|
||||||
|
controller_version: string | undefined
|
||||||
|
): UpdateButton => {
|
||||||
|
if (!isString(controller_version)) { return UpdateButton.none; }
|
||||||
|
if (!isString(candidate)) { return UpdateButton.unknown; }
|
||||||
|
|
||||||
|
switch (semverCompare(candidate, controller_version)) {
|
||||||
|
case SemverResult.RIGHT_IS_GREATER:
|
||||||
|
case SemverResult.EQUAL:
|
||||||
|
return UpdateButton.upToDate;
|
||||||
|
default:
|
||||||
|
return UpdateButton.needsUpdate;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OsUpdateButton = (props: OsUpdateButtonProps) => {
|
||||||
const { bot, sourceFbosConfig, botOnline } = props;
|
const { bot, sourceFbosConfig, botOnline } = props;
|
||||||
let buttonStatus = UpdateButton.none;
|
let buttonStatus = UpdateButton.none;
|
||||||
|
|
||||||
|
/** FBOS beta release opt-in setting. */
|
||||||
const betaOptIn = sourceFbosConfig("beta_opt_in").value;
|
const betaOptIn = sourceFbosConfig("beta_opt_in").value;
|
||||||
|
// Information about available releases.
|
||||||
const { currentOSVersion, currentBetaOSVersion, currentBetaOSCommit } = bot;
|
const { currentOSVersion, currentBetaOSVersion, currentBetaOSCommit } = bot;
|
||||||
const { commit } = bot.hardware.informational_settings;
|
// Currently installed FBOS version data.
|
||||||
const latestReleaseV = betaOptIn
|
const { controller_version, commit } = bot.hardware.informational_settings;
|
||||||
? currentBetaOSVersion
|
|
||||||
: currentOSVersion;
|
/** Newest release version, given settings and data available. */
|
||||||
const { controller_version } = bot.hardware.informational_settings;
|
const latestReleaseV =
|
||||||
if (_.isString(latestReleaseV) && _.isString(controller_version)) {
|
getLatestVersion(currentOSVersion, currentBetaOSVersion, !!betaOptIn);
|
||||||
switch (semverCompare(latestReleaseV, controller_version)) {
|
// Set FBOS update button status.
|
||||||
case SemverResult.RIGHT_IS_GREATER:
|
buttonStatus = compareWithBotVersion(latestReleaseV, controller_version);
|
||||||
case SemverResult.EQUAL:
|
|
||||||
buttonStatus = UpdateButton.upToDate;
|
// Button status modification for beta release edge cases.
|
||||||
break;
|
if ((buttonStatus === UpdateButton.upToDate) && betaOptIn &&
|
||||||
default:
|
latestReleaseV === currentBetaOSVersion &&
|
||||||
buttonStatus = UpdateButton.needsUpdate;
|
!betaCommitsAreEqual(commit, currentBetaOSCommit)) {
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!_.isString(latestReleaseV)) {
|
|
||||||
buttonStatus = betaOptIn
|
|
||||||
? UpdateButton.unknownBeta
|
|
||||||
: UpdateButton.unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (betaOptIn
|
|
||||||
&& _.isString(commit) && _.isString(currentBetaOSCommit)
|
|
||||||
&& commit !== currentBetaOSCommit) {
|
|
||||||
buttonStatus = UpdateButton.needsUpdate;
|
buttonStatus = UpdateButton.needsUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** FBOS update download progress data. */
|
||||||
const osUpdateJob = (bot.hardware.jobs || {})["FBOS_OTA"];
|
const osUpdateJob = (bot.hardware.jobs || {})["FBOS_OTA"];
|
||||||
|
|
||||||
const isWorking = (job: JobProgress | undefined) =>
|
|
||||||
job && (job.status == "working");
|
|
||||||
|
|
||||||
function downloadProgress(job: JobProgress | undefined) {
|
|
||||||
if (job && isWorking(job)) {
|
|
||||||
switch (job.unit) {
|
|
||||||
case ("bytes"):
|
|
||||||
const kiloBytes = Math.round(job.bytes / 1024);
|
|
||||||
const megaBytes = Math.round(job.bytes / 1048576);
|
|
||||||
if (kiloBytes < 1) {
|
|
||||||
return job.bytes + "B";
|
|
||||||
} else if (megaBytes < 1) {
|
|
||||||
return kiloBytes + "kB";
|
|
||||||
} else {
|
|
||||||
return megaBytes + "MB";
|
|
||||||
}
|
|
||||||
case ("percent"):
|
|
||||||
return job.percent + "%";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return <button
|
return <button
|
||||||
className={"fb-button " + buttonProps(buttonStatus).color}
|
className={"fb-button " + buttonProps(buttonStatus).color}
|
||||||
title={latestReleaseV}
|
title={latestReleaseV}
|
||||||
disabled={isWorking(osUpdateJob) || !botOnline}
|
disabled={isWorking(osUpdateJob) || !botOnline}
|
||||||
onClick={() => checkControllerUpdates()}>
|
onClick={checkControllerUpdates}>
|
||||||
{downloadProgress(osUpdateJob) || buttonProps(buttonStatus).text}
|
{downloadProgress(osUpdateJob) || buttonProps(buttonStatus).text}
|
||||||
</button>;
|
</button>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -84,7 +84,7 @@ export class Logs extends React.Component<LogsProps, Partial<LogsState>> {
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={2}>
|
<Col xs={2}>
|
||||||
<div className={"settings-menu-button"}>
|
<div className={"settings-menu-button"}>
|
||||||
<Popover position={Position.BOTTOM_RIGHT}>
|
<Popover position={Position.TOP_RIGHT}>
|
||||||
<i className="fa fa-gear" />
|
<i className="fa fa-gear" />
|
||||||
<LogsSettingsMenu
|
<LogsSettingsMenu
|
||||||
setFilterLevel={this.setFilterLevel}
|
setFilterLevel={this.setFilterLevel}
|
||||||
|
@ -93,7 +93,7 @@ export class Logs extends React.Component<LogsProps, Partial<LogsState>> {
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
<div className={"settings-menu-button"}>
|
<div className={"settings-menu-button"}>
|
||||||
<Popover position={Position.BOTTOM_RIGHT}>
|
<Popover position={Position.TOP_RIGHT}>
|
||||||
<button className={`fb-button ${filterBtnColor}`}>
|
<button className={`fb-button ${filterBtnColor}`}>
|
||||||
{this.filterActive ? t("Filters active") : t("filter")}
|
{this.filterActive ? t("Filters active") : t("filter")}
|
||||||
</button>
|
</button>
|
||||||
|
|
Loading…
Reference in New Issue