add none camera type
parent
4fa48cb74b
commit
22465a5558
|
@ -0,0 +1,17 @@
|
|||
import { HardwareFlags, FarmwareData } from "../sequences/interfaces";
|
||||
|
||||
export const fakeHardwareFlags = (): HardwareFlags => ({
|
||||
findHomeEnabled: { x: false, y: false, z: false },
|
||||
stopAtHome: { x: false, y: false, z: false },
|
||||
stopAtMax: { x: false, y: false, z: false },
|
||||
negativeOnly: { x: false, y: false, z: false },
|
||||
axisLength: { x: 0, y: 0, z: 0 },
|
||||
});
|
||||
|
||||
export const fakeFarmwareData = (): FarmwareData => ({
|
||||
farmwareNames: [],
|
||||
firstPartyFarmwareNames: [],
|
||||
showFirstPartyFarmware: false,
|
||||
farmwareConfigs: {},
|
||||
cameraDisabled: false,
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
import { HardwareFlags } from "../sequences/interfaces";
|
||||
|
||||
export const fakeHardwareFlags = (): HardwareFlags => {
|
||||
return {
|
||||
findHomeEnabled: { x: false, y: false, z: false },
|
||||
stopAtHome: { x: false, y: false, z: false },
|
||||
stopAtMax: { x: false, y: false, z: false },
|
||||
negativeOnly: { x: false, y: false, z: false },
|
||||
axisLength: { x: 0, y: 0, z: 0 },
|
||||
};
|
||||
};
|
|
@ -9,7 +9,7 @@ import { RawApp as App, AppProps, mapStateToProps } from "../app";
|
|||
import { mount } from "enzyme";
|
||||
import { bot } from "../__test_support__/fake_state/bot";
|
||||
import {
|
||||
fakeUser, fakeWebAppConfig
|
||||
fakeUser, fakeWebAppConfig, fakeFbosConfig, fakeFarmwareEnv
|
||||
} from "../__test_support__/fake_state/resources";
|
||||
import { fakeState } from "../__test_support__/fake_state";
|
||||
import {
|
||||
|
@ -40,7 +40,8 @@ const fakeProps = (): AppProps => ({
|
|||
resources: buildResourceIndex().index,
|
||||
autoSync: false,
|
||||
alertCount: 0,
|
||||
pings: fakePings()
|
||||
pings: fakePings(),
|
||||
env: {},
|
||||
});
|
||||
|
||||
describe("<App />: Controls Pop-Up", () => {
|
||||
|
@ -145,7 +146,24 @@ describe("mapStateToProps()", () => {
|
|||
const config = fakeWebAppConfig();
|
||||
config.body.x_axis_inverted = true;
|
||||
state.resources = buildResourceIndex([config]);
|
||||
state.bot.hardware.user_env = { fake: "value" };
|
||||
const result = mapStateToProps(state);
|
||||
expect(result.axisInversion.x).toEqual(true);
|
||||
expect(result.autoSync).toEqual(false);
|
||||
expect(result.env).toEqual({ fake: "value" });
|
||||
});
|
||||
|
||||
it("returns api props", () => {
|
||||
const state = fakeState();
|
||||
const config = fakeFbosConfig();
|
||||
config.body.auto_sync = true;
|
||||
config.body.api_migrated = true;
|
||||
const fakeEnv = fakeFarmwareEnv();
|
||||
state.resources = buildResourceIndex([config, fakeEnv]);
|
||||
state.bot.minOsFeatureData = { api_farmware_env: "8.0.0" };
|
||||
state.bot.hardware.informational_settings.controller_version = "8.0.0";
|
||||
const result = mapStateToProps(state);
|
||||
expect(result.autoSync).toEqual(true);
|
||||
expect(result.env).toEqual({ [fakeEnv.body.key]: fakeEnv.body.value });
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,38 +9,37 @@ import { ControlsPopup } from "../controls_popup";
|
|||
import { mount } from "enzyme";
|
||||
import { bot } from "../__test_support__/fake_state/bot";
|
||||
import { ControlsPopupProps } from "../controls/move/interfaces";
|
||||
import { error } from "../toast/toast";
|
||||
import { Content, ToolTips } from "../constants";
|
||||
|
||||
describe("<ControlsPopup />", () => {
|
||||
const fakeProps = (): ControlsPopupProps => {
|
||||
return {
|
||||
dispatch: jest.fn(),
|
||||
axisInversion: { x: false, y: false, z: false },
|
||||
botPosition: { x: undefined, y: undefined, z: undefined },
|
||||
firmwareSettings: bot.hardware.mcu_params,
|
||||
xySwap: false,
|
||||
arduinoBusy: false,
|
||||
stepSize: 100,
|
||||
botOnline: true,
|
||||
};
|
||||
};
|
||||
|
||||
const p = fakeProps();
|
||||
p.axisInversion.x = true;
|
||||
const wrapper = mount(<ControlsPopup {...p} />);
|
||||
|
||||
afterAll(wrapper.unmount);
|
||||
const fakeProps = (): ControlsPopupProps => ({
|
||||
dispatch: jest.fn(),
|
||||
axisInversion: { x: true, y: false, z: false },
|
||||
botPosition: { x: undefined, y: undefined, z: undefined },
|
||||
firmwareSettings: bot.hardware.mcu_params,
|
||||
xySwap: false,
|
||||
arduinoBusy: false,
|
||||
stepSize: 100,
|
||||
botOnline: true,
|
||||
env: {},
|
||||
});
|
||||
|
||||
it("Has a false initial state", () => {
|
||||
const wrapper = mount(<ControlsPopup {...fakeProps()} />);
|
||||
expect(wrapper.state("isOpen")).toBeFalsy();
|
||||
});
|
||||
|
||||
it("Toggles state", () => {
|
||||
const wrapper = mount(<ControlsPopup {...fakeProps()} />);
|
||||
const parent = wrapper.find("i").first();
|
||||
parent.simulate("click");
|
||||
expect(wrapper.state("isOpen")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("x axis is inverted", () => {
|
||||
const wrapper = mount(<ControlsPopup {...fakeProps()} />);
|
||||
wrapper.setState({ isOpen: true });
|
||||
const button = wrapper.find("button").at(3);
|
||||
expect(button.props().title).toBe("move x axis (100)");
|
||||
button.simulate("click");
|
||||
|
@ -49,6 +48,8 @@ describe("<ControlsPopup />", () => {
|
|||
});
|
||||
|
||||
it("y axis is not inverted", () => {
|
||||
const wrapper = mount(<ControlsPopup {...fakeProps()} />);
|
||||
wrapper.setState({ isOpen: true });
|
||||
const button = wrapper.find("button").at(1);
|
||||
expect(button.props().title).toBe("move y axis (100)");
|
||||
button.simulate("click");
|
||||
|
@ -57,6 +58,7 @@ describe("<ControlsPopup />", () => {
|
|||
});
|
||||
|
||||
it("disabled when closed", () => {
|
||||
const wrapper = mount(<ControlsPopup {...fakeProps()} />);
|
||||
wrapper.setState({ isOpen: false });
|
||||
[0, 1, 2, 3].map((i) => wrapper.find("button").at(i).simulate("click"));
|
||||
expect(mockDevice.moveRelative).not.toHaveBeenCalled();
|
||||
|
@ -65,6 +67,7 @@ describe("<ControlsPopup />", () => {
|
|||
it("swaps axes", () => {
|
||||
const swappedProps = fakeProps();
|
||||
swappedProps.xySwap = true;
|
||||
swappedProps.axisInversion.x = false;
|
||||
const swapped = mount(<ControlsPopup {...swappedProps} />);
|
||||
swapped.setState({ isOpen: true });
|
||||
expect(swapped.state("isOpen")).toBeTruthy();
|
||||
|
@ -76,7 +79,25 @@ describe("<ControlsPopup />", () => {
|
|||
});
|
||||
|
||||
it("takes photo", () => {
|
||||
wrapper.find("button").at(4).simulate("click");
|
||||
const wrapper = mount(<ControlsPopup {...fakeProps()} />);
|
||||
wrapper.setState({ isOpen: true });
|
||||
const btn = wrapper.find("button").at(4);
|
||||
expect(btn.props().title).not.toEqual(Content.NO_CAMERA_SELECTED);
|
||||
btn.simulate("click");
|
||||
expect(mockDevice.takePhoto).toHaveBeenCalled();
|
||||
expect(error).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows camera as disabled", () => {
|
||||
const p = fakeProps();
|
||||
p.env = { camera: "NONE" };
|
||||
const wrapper = mount(<ControlsPopup {...p} />);
|
||||
wrapper.setState({ isOpen: true });
|
||||
const btn = wrapper.find("button").at(4);
|
||||
expect(btn.props().title).toEqual(Content.NO_CAMERA_SELECTED);
|
||||
btn.simulate("click");
|
||||
expect(error).toHaveBeenCalledWith(
|
||||
ToolTips.SELECT_A_CAMERA, Content.NO_CAMERA_SELECTED);
|
||||
expect(mockDevice.takePhoto).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@ import { init, error } from "./toast/toast";
|
|||
import { NavBar } from "./nav";
|
||||
import { Everything, TimeSettings } from "./interfaces";
|
||||
import { LoadingPlant } from "./loading_plant";
|
||||
import { BotState, Xyz } from "./devices/interfaces";
|
||||
import { BotState, Xyz, UserEnv } from "./devices/interfaces";
|
||||
import { ResourceName, TaggedUser, TaggedLog } from "farmbot";
|
||||
import {
|
||||
maybeFetchUser,
|
||||
|
@ -30,6 +30,7 @@ import { isBotOnline } from "./devices/must_be_online";
|
|||
import { getStatus } from "./connectivity/reducer_support";
|
||||
import { getAllAlerts } from "./messages/state_to_props";
|
||||
import { PingDictionary } from "./devices/connectivity/qos";
|
||||
import { getEnv, getShouldDisplayFn } from "./farmware/state_to_props";
|
||||
|
||||
/** For the logger module */
|
||||
init();
|
||||
|
@ -52,11 +53,14 @@ export interface AppProps {
|
|||
autoSync: boolean;
|
||||
alertCount: number;
|
||||
pings: PingDictionary;
|
||||
env: UserEnv;
|
||||
}
|
||||
|
||||
export function mapStateToProps(props: Everything): AppProps {
|
||||
const webAppConfigValue = getWebAppConfigValue(() => props);
|
||||
const fbosConfig = validFbosConfig(getFbosConfig(props.resources.index));
|
||||
const shouldDisplay = getShouldDisplayFn(props.resources.index, props.bot);
|
||||
const env = getEnv(props.resources.index, shouldDisplay, props.bot);
|
||||
return {
|
||||
timeSettings: maybeGetTimeSettings(props.resources.index),
|
||||
dispatch: props.dispatch,
|
||||
|
@ -78,7 +82,8 @@ export function mapStateToProps(props: Everything): AppProps {
|
|||
resources: props.resources.index,
|
||||
autoSync: !!(fbosConfig && fbosConfig.auto_sync),
|
||||
alertCount: getAllAlerts(props.resources).length,
|
||||
pings: props.bot.connectivity.pings
|
||||
pings: props.bot.connectivity.pings,
|
||||
env,
|
||||
};
|
||||
}
|
||||
/** Time at which the app gives up and asks the user to refresh */
|
||||
|
@ -147,6 +152,7 @@ export class RawApp extends React.Component<AppProps, {}> {
|
|||
xySwap={this.props.xySwap}
|
||||
arduinoBusy={!!this.props.bot.hardware.informational_settings.busy}
|
||||
botOnline={isBotOnline(sync_status, getStatus(bot2mqtt))}
|
||||
env={this.props.env}
|
||||
stepSize={this.props.bot.stepSize} />}
|
||||
</div>;
|
||||
}
|
||||
|
|
|
@ -285,6 +285,9 @@ export namespace ToolTips {
|
|||
trim(`Snaps a photo using the device camera. Select the camera type
|
||||
on the Device page.`);
|
||||
|
||||
export const SELECT_A_CAMERA =
|
||||
trim(`Select a camera on the Device page to take photos.`);
|
||||
|
||||
export const MARK_AS =
|
||||
trim(`The Mark As step allows FarmBot to programmatically edit the
|
||||
properties of the UTM, plants, and weeds from within a sequence.
|
||||
|
@ -822,6 +825,9 @@ export namespace Content {
|
|||
|
||||
export const NOT_AVAILABLE_WHEN_OFFLINE =
|
||||
trim(`Not available when device is offline.`);
|
||||
|
||||
export const NO_CAMERA_SELECTED =
|
||||
trim(`No camera selected`);
|
||||
}
|
||||
|
||||
export namespace TourContent {
|
||||
|
|
|
@ -24,6 +24,7 @@ describe("<Controls />", () => {
|
|||
getWebAppConfigVal: jest.fn((key) => (mockConfig[key])),
|
||||
sensorReadings: [],
|
||||
timeSettings: fakeTimeSettings(),
|
||||
env: {},
|
||||
});
|
||||
|
||||
it("shows webcam widget", () => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { mapStateToProps } from "../state_to_props";
|
||||
import { buildResourceIndex } from "../../__test_support__/resource_index_builder";
|
||||
import { fakeUser } from "../../__test_support__/fake_state/resources";
|
||||
import { fakeUser, fakeFarmwareEnv } from "../../__test_support__/fake_state/resources";
|
||||
import { fakeState } from "../../__test_support__/fake_state";
|
||||
|
||||
describe("mapStateToProps()", () => {
|
||||
|
@ -10,4 +10,14 @@ describe("mapStateToProps()", () => {
|
|||
const result = mapStateToProps(state);
|
||||
expect(result.timeSettings).toEqual({ utcOffset: 0, hour24: false });
|
||||
});
|
||||
|
||||
it("returns api props", () => {
|
||||
const state = fakeState();
|
||||
const fakeEnv = fakeFarmwareEnv();
|
||||
state.resources = buildResourceIndex([fakeEnv]);
|
||||
state.bot.minOsFeatureData = { api_farmware_env: "8.0.0" };
|
||||
state.bot.hardware.informational_settings.controller_version = "8.0.0";
|
||||
const result = mapStateToProps(state);
|
||||
expect(result.env).toEqual({ [fakeEnv.body.key]: fakeEnv.body.value });
|
||||
});
|
||||
});
|
||||
|
|
|
@ -29,6 +29,7 @@ export class RawControls extends React.Component<Props, {}> {
|
|||
|
||||
move = () => <Move
|
||||
bot={this.props.bot}
|
||||
env={this.props.env}
|
||||
dispatch={this.props.dispatch}
|
||||
arduinoBusy={this.arduinoBusy}
|
||||
botToMqttStatus={this.props.botToMqttStatus}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { BotState, Xyz, BotPosition, ShouldDisplay } from "../devices/interfaces";
|
||||
import {
|
||||
BotState, Xyz, BotPosition, ShouldDisplay, UserEnv
|
||||
} from "../devices/interfaces";
|
||||
import { Vector3, McuParams } from "farmbot/dist";
|
||||
import {
|
||||
TaggedWebcamFeed,
|
||||
|
@ -22,6 +24,7 @@ export interface Props {
|
|||
getWebAppConfigVal: GetWebAppConfigValue;
|
||||
sensorReadings: TaggedSensorReading[];
|
||||
timeSettings: TimeSettings;
|
||||
env: UserEnv;
|
||||
}
|
||||
|
||||
export interface AxisDisplayGroupProps {
|
||||
|
|
|
@ -15,6 +15,8 @@ import { mount } from "enzyme";
|
|||
import { JogButtons } from "../jog_buttons";
|
||||
import { JogMovementControlsProps } from "../interfaces";
|
||||
import { bot } from "../../../__test_support__/fake_state/bot";
|
||||
import { error } from "../../../toast/toast";
|
||||
import { Content, ToolTips } from "../../../constants";
|
||||
|
||||
describe("<JogButtons/>", function () {
|
||||
const jogButtonProps = (): JogMovementControlsProps => {
|
||||
|
@ -26,6 +28,7 @@ describe("<JogButtons/>", function () {
|
|||
firmwareSettings: bot.hardware.mcu_params,
|
||||
xySwap: false,
|
||||
doFindHome: false,
|
||||
env: {},
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -60,8 +63,12 @@ describe("<JogButtons/>", function () {
|
|||
|
||||
it("takes photo", () => {
|
||||
const jogButtons = mount(<JogButtons {...jogButtonProps()} />);
|
||||
jogButtons.find("button").at(0).simulate("click");
|
||||
const cameraBtn = jogButtons.find("button").at(0);
|
||||
expect(cameraBtn.props().title).not.toEqual(Content.NO_CAMERA_SELECTED);
|
||||
|
||||
cameraBtn.simulate("click");
|
||||
expect(mockDevice.takePhoto).toHaveBeenCalled();
|
||||
expect(error).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("error taking photo", () => {
|
||||
|
@ -71,6 +78,18 @@ describe("<JogButtons/>", function () {
|
|||
expect(mockDevice.takePhoto).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows camera as disabled", () => {
|
||||
const p = jogButtonProps();
|
||||
p.env = { camera: "NONE" };
|
||||
const jogButtons = mount(<JogButtons {...p} />);
|
||||
const cameraBtn = jogButtons.find("button").at(0);
|
||||
expect(cameraBtn.props().title).toEqual(Content.NO_CAMERA_SELECTED);
|
||||
cameraBtn.simulate("click");
|
||||
expect(error).toHaveBeenCalledWith(
|
||||
ToolTips.SELECT_A_CAMERA, Content.NO_CAMERA_SELECTED);
|
||||
expect(mockDevice.takePhoto).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("has unswapped xy jog buttons", () => {
|
||||
const jogButtons = mount(<JogButtons {...jogButtonProps()} />);
|
||||
const button = jogButtons.find("button").at(6);
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
const mockDevice = {
|
||||
moveAbsolute: jest.fn(() => { return Promise.resolve(); }),
|
||||
};
|
||||
const mockDevice = { moveAbsolute: jest.fn(() => Promise.resolve()) };
|
||||
jest.mock("../../../device", () => ({ getDevice: () => mockDevice }));
|
||||
|
||||
jest.mock("../../../device", () => ({
|
||||
getDevice: () => (mockDevice)
|
||||
jest.mock("../../../config_storage/actions", () => ({
|
||||
toggleWebAppBool: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("../../../config_storage/actions", () => {
|
||||
return {
|
||||
toggleWebAppBool: jest.fn()
|
||||
};
|
||||
});
|
||||
jest.mock("../../../account/dev/dev_support", () => ({
|
||||
DevSettings: {
|
||||
futureFeaturesEnabled: () => false,
|
||||
}
|
||||
}));
|
||||
|
||||
import * as React from "react";
|
||||
import { mount, shallow } from "enzyme";
|
||||
|
@ -26,16 +25,15 @@ import { clickButton } from "../../../__test_support__/helpers";
|
|||
describe("<Move />", () => {
|
||||
const mockConfig: Dictionary<boolean> = {};
|
||||
|
||||
function fakeProps(): MoveProps {
|
||||
return {
|
||||
dispatch: jest.fn(),
|
||||
bot: bot,
|
||||
arduinoBusy: false,
|
||||
botToMqttStatus: "up",
|
||||
firmwareSettings: bot.hardware.mcu_params,
|
||||
getWebAppConfigVal: jest.fn((key) => (mockConfig[key])),
|
||||
};
|
||||
}
|
||||
const fakeProps = (): MoveProps => ({
|
||||
dispatch: jest.fn(),
|
||||
bot: bot,
|
||||
arduinoBusy: false,
|
||||
botToMqttStatus: "up",
|
||||
firmwareSettings: bot.hardware.mcu_params,
|
||||
getWebAppConfigVal: jest.fn((key) => (mockConfig[key])),
|
||||
env: {},
|
||||
});
|
||||
|
||||
it("has default elements", () => {
|
||||
const wrapper = mount(<Move {...fakeProps()} />);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { BotPosition, BotState } from "../../devices/interfaces";
|
||||
import { BotPosition, BotState, UserEnv } from "../../devices/interfaces";
|
||||
import { McuParams, Xyz } from "farmbot";
|
||||
import { NetworkState } from "../../connectivity/interfaces";
|
||||
import { GetWebAppConfigValue } from "../../config_storage/actions";
|
||||
|
@ -14,6 +14,7 @@ export interface MoveProps {
|
|||
botToMqttStatus: NetworkState;
|
||||
firmwareSettings: McuParams;
|
||||
getWebAppConfigVal: GetWebAppConfigValue;
|
||||
env: UserEnv;
|
||||
}
|
||||
|
||||
export interface DirectionButtonProps {
|
||||
|
@ -47,6 +48,7 @@ interface JogMovementControlsBaseProps extends DirectionAxesProps {
|
|||
stepSize: number;
|
||||
arduinoBusy: boolean;
|
||||
xySwap: boolean;
|
||||
env: UserEnv;
|
||||
}
|
||||
|
||||
export interface JogMovementControlsProps extends JogMovementControlsBaseProps {
|
||||
|
|
|
@ -5,6 +5,9 @@ import { JogMovementControlsProps } from "./interfaces";
|
|||
import { getDevice } from "../../device";
|
||||
import { buildDirectionProps } from "./direction_axes_props";
|
||||
import { t } from "../../i18next_wrapper";
|
||||
import {
|
||||
cameraBtnProps
|
||||
} from "../../devices/components/fbos_settings/camera_selection";
|
||||
const DEFAULT_STEP_SIZE = 100;
|
||||
|
||||
/*
|
||||
|
@ -20,35 +23,37 @@ export function JogButtons(props: JogMovementControlsProps) {
|
|||
const directionAxesProps = buildDirectionProps(props);
|
||||
const rightLeft = xySwap ? "y" : "x";
|
||||
const upDown = xySwap ? "x" : "y";
|
||||
|
||||
const commonProps = {
|
||||
steps: stepSize || DEFAULT_STEP_SIZE,
|
||||
disabled: arduinoBusy
|
||||
};
|
||||
const camDisabled = cameraBtnProps(props.env);
|
||||
return <table className="jog-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<button
|
||||
className="i fa fa-camera arrow-button fb-button"
|
||||
title={t("Take a photo")}
|
||||
onClick={() => getDevice().takePhoto().catch(() => { })} />
|
||||
className={`fa fa-camera arrow-button fb-button ${
|
||||
camDisabled.class}`}
|
||||
title={camDisabled.title || t("Take a photo")}
|
||||
onClick={camDisabled.click ||
|
||||
(() => getDevice().takePhoto().catch(() => { }))} />
|
||||
</td>
|
||||
<td />
|
||||
<td />
|
||||
<td>
|
||||
<DirectionButton
|
||||
<DirectionButton {...commonProps}
|
||||
axis={upDown}
|
||||
direction="up"
|
||||
directionAxisProps={directionAxesProps[upDown]}
|
||||
steps={stepSize || DEFAULT_STEP_SIZE}
|
||||
disabled={arduinoBusy} />
|
||||
directionAxisProps={directionAxesProps[upDown]} />
|
||||
</td>
|
||||
<td />
|
||||
<td />
|
||||
<td>
|
||||
<DirectionButton
|
||||
<DirectionButton {...commonProps}
|
||||
axis="z"
|
||||
direction="up"
|
||||
directionAxisProps={directionAxesProps.z}
|
||||
steps={stepSize || DEFAULT_STEP_SIZE}
|
||||
disabled={arduinoBusy} />
|
||||
directionAxisProps={directionAxesProps.z} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -61,37 +66,29 @@ export function JogButtons(props: JogMovementControlsProps) {
|
|||
</td>
|
||||
<td />
|
||||
<td>
|
||||
<DirectionButton
|
||||
<DirectionButton {...commonProps}
|
||||
axis={rightLeft}
|
||||
direction="left"
|
||||
directionAxisProps={directionAxesProps[rightLeft]}
|
||||
steps={stepSize || DEFAULT_STEP_SIZE}
|
||||
disabled={arduinoBusy} />
|
||||
directionAxisProps={directionAxesProps[rightLeft]} />
|
||||
</td>
|
||||
<td>
|
||||
<DirectionButton
|
||||
<DirectionButton {...commonProps}
|
||||
axis={upDown}
|
||||
direction="down"
|
||||
directionAxisProps={directionAxesProps[upDown]}
|
||||
steps={stepSize || DEFAULT_STEP_SIZE}
|
||||
disabled={arduinoBusy} />
|
||||
directionAxisProps={directionAxesProps[upDown]} />
|
||||
</td>
|
||||
<td>
|
||||
<DirectionButton
|
||||
<DirectionButton {...commonProps}
|
||||
axis={rightLeft}
|
||||
direction="right"
|
||||
directionAxisProps={directionAxesProps[rightLeft]}
|
||||
steps={stepSize || DEFAULT_STEP_SIZE}
|
||||
disabled={arduinoBusy} />
|
||||
directionAxisProps={directionAxesProps[rightLeft]} />
|
||||
</td>
|
||||
<td />
|
||||
<td>
|
||||
<DirectionButton
|
||||
<DirectionButton {...commonProps}
|
||||
axis="z"
|
||||
direction="down"
|
||||
directionAxisProps={directionAxesProps.z}
|
||||
steps={stepSize || DEFAULT_STEP_SIZE}
|
||||
disabled={arduinoBusy} />
|
||||
directionAxisProps={directionAxesProps.z} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react";
|
||||
import { McuParams } from "farmbot";
|
||||
import { BotPosition } from "../../devices/interfaces";
|
||||
import { BotPosition, UserEnv } from "../../devices/interfaces";
|
||||
import { changeStepSize } from "../../devices/actions";
|
||||
import { StepSizeSelector } from "./step_size_selector";
|
||||
import { GetWebAppBool } from "./interfaces";
|
||||
|
@ -15,6 +15,7 @@ interface JogControlsGroupProps {
|
|||
getValue: GetWebAppBool;
|
||||
arduinoBusy: boolean;
|
||||
firmwareSettings: McuParams;
|
||||
env: UserEnv;
|
||||
}
|
||||
|
||||
export const JogControlsGroup = (props: JogControlsGroupProps) => {
|
||||
|
@ -38,6 +39,7 @@ export const JogControlsGroup = (props: JogControlsGroupProps) => {
|
|||
z: getValue(BooleanSetting.z_axis_inverted)
|
||||
}}
|
||||
arduinoBusy={arduinoBusy}
|
||||
env={props.env}
|
||||
firmwareSettings={firmwareSettings}
|
||||
xySwap={getValue(BooleanSetting.xy_swap)}
|
||||
doFindHome={getValue(BooleanSetting.home_button_homing)} />
|
||||
|
|
|
@ -47,6 +47,7 @@ export class Move extends React.Component<MoveProps, {}> {
|
|||
botPosition={locationData.position}
|
||||
getValue={this.getValue}
|
||||
arduinoBusy={this.props.arduinoBusy}
|
||||
env={this.props.env}
|
||||
firmwareSettings={this.props.firmwareSettings} />
|
||||
<BotPositionRows
|
||||
locationData={locationData}
|
||||
|
|
|
@ -3,31 +3,23 @@ import {
|
|||
selectAllPeripherals,
|
||||
selectAllWebcamFeeds,
|
||||
selectAllSensors,
|
||||
maybeGetDevice,
|
||||
selectAllSensorReadings,
|
||||
maybeGetTimeSettings
|
||||
} from "../resources/selectors";
|
||||
import { Props } from "./interfaces";
|
||||
import {
|
||||
validFwConfig,
|
||||
createShouldDisplayFn as shouldDisplayFunc,
|
||||
determineInstalledOsVersion
|
||||
} from "../util";
|
||||
import { validFwConfig } from "../util";
|
||||
import { getWebAppConfigValue } from "../config_storage/actions";
|
||||
import { getFirmwareConfig } from "../resources/getters";
|
||||
import { uniq } from "lodash";
|
||||
import { getStatus } from "../connectivity/reducer_support";
|
||||
import { DevSettings } from "../account/dev/dev_support";
|
||||
import { getEnv, getShouldDisplayFn } from "../farmware/state_to_props";
|
||||
|
||||
export function mapStateToProps(props: Everything): Props {
|
||||
const fwConfig = validFwConfig(getFirmwareConfig(props.resources.index));
|
||||
const { mcu_params } = props.bot.hardware;
|
||||
|
||||
const device = maybeGetDevice(props.resources.index);
|
||||
const installedOsVersion = determineInstalledOsVersion(props.bot, device);
|
||||
const fbosVersionOverride = DevSettings.overriddenFbosVersion();
|
||||
const shouldDisplay = shouldDisplayFunc(
|
||||
installedOsVersion, props.bot.minOsFeatureData, fbosVersionOverride);
|
||||
const shouldDisplay = getShouldDisplayFn(props.resources.index, props.bot);
|
||||
const env = getEnv(props.resources.index, shouldDisplay, props.bot);
|
||||
|
||||
return {
|
||||
feeds: selectAllWebcamFeeds(props.resources.index),
|
||||
|
@ -41,5 +33,6 @@ export function mapStateToProps(props: Everything): Props {
|
|||
shouldDisplay,
|
||||
sensorReadings: selectAllSensorReadings(props.resources.index),
|
||||
timeSettings: maybeGetTimeSettings(props.resources.index),
|
||||
env,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,6 +5,10 @@ import { buildDirectionProps } from "./controls/move/direction_axes_props";
|
|||
import { ControlsPopupProps } from "./controls/move/interfaces";
|
||||
import { commandErr } from "./devices/actions";
|
||||
import { mapPanelClassName } from "./farm_designer/map/util";
|
||||
import {
|
||||
cameraBtnProps
|
||||
} from "./devices/components/fbos_settings/camera_selection";
|
||||
import { t } from "./i18next_wrapper";
|
||||
|
||||
interface State {
|
||||
isOpen: boolean;
|
||||
|
@ -23,40 +27,38 @@ export class ControlsPopup
|
|||
const directionAxesProps = buildDirectionProps(this.props);
|
||||
const rightLeft = xySwap ? "y" : "x";
|
||||
const upDown = xySwap ? "x" : "y";
|
||||
const movementDisabled = !isOpen || arduinoBusy || !botOnline;
|
||||
const commonProps = { steps: stepSize, disabled: movementDisabled };
|
||||
const camDisabled = cameraBtnProps(this.props.env);
|
||||
return <div
|
||||
className={`controls-popup ${isOpen} ${mapPanelClassName()}`}>
|
||||
<i className="fa fa-crosshairs"
|
||||
onClick={this.toggle("isOpen")} />
|
||||
<div className="controls-popup-menu-outer">
|
||||
<div className="controls-popup-menu-inner">
|
||||
<DirectionButton
|
||||
<DirectionButton {...commonProps}
|
||||
axis={rightLeft}
|
||||
direction="right"
|
||||
directionAxisProps={directionAxesProps[rightLeft]}
|
||||
steps={stepSize}
|
||||
disabled={!isOpen || arduinoBusy || !botOnline} />
|
||||
<DirectionButton
|
||||
directionAxisProps={directionAxesProps[rightLeft]} />
|
||||
<DirectionButton {...commonProps}
|
||||
axis={upDown}
|
||||
direction="up"
|
||||
directionAxisProps={directionAxesProps[upDown]}
|
||||
steps={stepSize}
|
||||
disabled={!isOpen || arduinoBusy || !botOnline} />
|
||||
<DirectionButton
|
||||
directionAxisProps={directionAxesProps[upDown]} />
|
||||
<DirectionButton {...commonProps}
|
||||
axis={upDown}
|
||||
direction="down"
|
||||
directionAxisProps={directionAxesProps[upDown]}
|
||||
steps={stepSize}
|
||||
disabled={!isOpen || arduinoBusy || !botOnline} />
|
||||
<DirectionButton
|
||||
directionAxisProps={directionAxesProps[upDown]} />
|
||||
<DirectionButton {...commonProps}
|
||||
axis={rightLeft}
|
||||
direction="left"
|
||||
directionAxisProps={directionAxesProps[rightLeft]}
|
||||
steps={stepSize}
|
||||
disabled={!isOpen || arduinoBusy || !botOnline} />
|
||||
directionAxisProps={directionAxesProps[rightLeft]} />
|
||||
<button
|
||||
className="i fa fa-camera arrow-button fb-button brown"
|
||||
disabled={!botOnline}
|
||||
onClick={() => getDevice().takePhoto().catch(commandErr("Photo"))} />
|
||||
className={`fa fa-camera arrow-button fb-button brown ${
|
||||
camDisabled.class}`}
|
||||
disabled={!isOpen || !botOnline}
|
||||
title={camDisabled.title || t("Take a photo")}
|
||||
onClick={camDisabled.click ||
|
||||
(() => getDevice().takePhoto().catch(commandErr("Photo")))} />
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
|
|
|
@ -279,6 +279,7 @@
|
|||
&.pseudo-disabled {
|
||||
background: $medium_light_gray !important;
|
||||
box-shadow: 0 2px 0px 0px lighten($medium_light_gray, 5%) !important;
|
||||
border-bottom: none !important;
|
||||
&:focus,
|
||||
&:hover,
|
||||
&.active {
|
||||
|
|
|
@ -1,26 +1,20 @@
|
|||
const mockDevice = {
|
||||
setUserEnv: jest.fn(() => { return Promise.resolve(); }),
|
||||
};
|
||||
jest.mock("../../../../device", () => ({
|
||||
getDevice: () => mockDevice
|
||||
}));
|
||||
const mockDevice = { setUserEnv: jest.fn(() => Promise.resolve()) };
|
||||
jest.mock("../../../../device", () => ({ getDevice: () => mockDevice }));
|
||||
|
||||
import * as React from "react";
|
||||
import { mount, shallow } from "enzyme";
|
||||
import { CameraSelection } from "../camera_selection";
|
||||
import { CameraSelection, cameraDisabled } from "../camera_selection";
|
||||
import { CameraSelectionProps } from "../interfaces";
|
||||
import { info, error } from "../../../../toast/toast";
|
||||
|
||||
describe("<CameraSelection/>", () => {
|
||||
const fakeProps = (): CameraSelectionProps => {
|
||||
return {
|
||||
env: {},
|
||||
botOnline: true,
|
||||
shouldDisplay: () => false,
|
||||
saveFarmwareEnv: jest.fn(),
|
||||
dispatch: jest.fn(),
|
||||
};
|
||||
};
|
||||
const fakeProps = (): CameraSelectionProps => ({
|
||||
env: {},
|
||||
botOnline: true,
|
||||
shouldDisplay: () => false,
|
||||
saveFarmwareEnv: jest.fn(),
|
||||
dispatch: jest.fn(),
|
||||
});
|
||||
|
||||
it("doesn't render camera", () => {
|
||||
const cameraSelection = mount(<CameraSelection {...fakeProps()} />);
|
||||
|
@ -66,3 +60,15 @@ describe("<CameraSelection/>", () => {
|
|||
expect(p.saveFarmwareEnv).toHaveBeenCalledWith("camera", "\"mycamera\"");
|
||||
});
|
||||
});
|
||||
|
||||
describe("cameraDisabled()", () => {
|
||||
it("returns enabled", () => {
|
||||
expect(cameraDisabled({ camera: "USB" })).toEqual(false);
|
||||
expect(cameraDisabled({ camera: "" })).toEqual(false);
|
||||
});
|
||||
|
||||
it("returns disabled", () => {
|
||||
expect(cameraDisabled({ camera: "none" })).toEqual(true);
|
||||
expect(cameraDisabled({ camera: "\"NONE\"" })).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,25 +6,56 @@ import {
|
|||
import { info, success, error } from "../../../toast/toast";
|
||||
import { getDevice } from "../../../device";
|
||||
import { ColWidth } from "../farmbot_os_settings";
|
||||
import { Feature } from "../../interfaces";
|
||||
import { Feature, UserEnv } from "../../interfaces";
|
||||
import { t } from "../../../i18next_wrapper";
|
||||
import { Content, ToolTips } from "../../../constants";
|
||||
|
||||
/** Check if the camera has been disabled. */
|
||||
export const cameraDisabled = (env: UserEnv): boolean =>
|
||||
parseCameraSelection(env) === Camera.NONE;
|
||||
|
||||
/** `disabled` and `title` props for buttons with actions that use the camera. */
|
||||
export const cameraBtnProps = (env: UserEnv) => {
|
||||
const disabled = cameraDisabled(env);
|
||||
return disabled
|
||||
? {
|
||||
class: "pseudo-disabled",
|
||||
click: () =>
|
||||
error(t(ToolTips.SELECT_A_CAMERA), t(Content.NO_CAMERA_SELECTED)),
|
||||
title: t(Content.NO_CAMERA_SELECTED)
|
||||
}
|
||||
: { class: "", click: undefined, title: "" };
|
||||
};
|
||||
|
||||
enum Camera {
|
||||
USB = "USB",
|
||||
RPI = "RPI",
|
||||
NONE = "NONE",
|
||||
}
|
||||
|
||||
const parseCameraSelection = (env: UserEnv): Camera => {
|
||||
const camera = env["camera"]?.toUpperCase();
|
||||
if (camera?.includes(Camera.NONE)) {
|
||||
return Camera.NONE;
|
||||
} else if (camera?.includes(Camera.RPI)) {
|
||||
return Camera.RPI;
|
||||
} else {
|
||||
return Camera.USB;
|
||||
}
|
||||
};
|
||||
|
||||
const CAMERA_CHOICES = () => ([
|
||||
{ label: t("USB Camera"), value: "USB" },
|
||||
{ label: t("Raspberry Pi Camera"), value: "RPI" }
|
||||
{ label: t("USB Camera"), value: Camera.USB },
|
||||
{ label: t("Raspberry Pi Camera"), value: Camera.RPI },
|
||||
{ label: t("None"), value: Camera.NONE },
|
||||
]);
|
||||
|
||||
const CAMERA_CHOICES_DDI = () => {
|
||||
const CHOICES = CAMERA_CHOICES();
|
||||
return {
|
||||
[CHOICES[0].value]: {
|
||||
label: CHOICES[0].label,
|
||||
value: CHOICES[0].value
|
||||
},
|
||||
[CHOICES[1].value]: {
|
||||
label: CHOICES[1].label,
|
||||
value: CHOICES[1].value
|
||||
}
|
||||
[CHOICES[0].value]: { label: CHOICES[0].label, value: CHOICES[0].value },
|
||||
[CHOICES[1].value]: { label: CHOICES[1].label, value: CHOICES[1].value },
|
||||
[CHOICES[2].value]: { label: CHOICES[2].label, value: CHOICES[2].value },
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -35,12 +66,8 @@ export class CameraSelection
|
|||
cameraStatus: ""
|
||||
};
|
||||
|
||||
selectedCamera(): DropDownItem {
|
||||
const camera = this.props.env["camera"];
|
||||
return camera
|
||||
? CAMERA_CHOICES_DDI()[JSON.parse(camera)]
|
||||
: CAMERA_CHOICES_DDI()["USB"];
|
||||
}
|
||||
selectedCamera = (): DropDownItem =>
|
||||
CAMERA_CHOICES_DDI()[parseCameraSelection(this.props.env)]
|
||||
|
||||
sendOffConfig = (selectedCamera: DropDownItem) => {
|
||||
const { props } = this;
|
||||
|
|
|
@ -209,8 +209,8 @@ export interface SensorsProps {
|
|||
|
||||
export interface FarmwareProps {
|
||||
dispatch: Function;
|
||||
env: Partial<WD_ENV>;
|
||||
user_env: UserEnv;
|
||||
wDEnv: Partial<WD_ENV>;
|
||||
env: UserEnv;
|
||||
images: TaggedImage[];
|
||||
currentImage: TaggedImage | undefined;
|
||||
botToMqttStatus: NetworkState;
|
||||
|
|
|
@ -1,38 +1,28 @@
|
|||
import { Everything } from "../interfaces";
|
||||
import { Props, Feature } from "./interfaces";
|
||||
import { Props } from "./interfaces";
|
||||
import {
|
||||
selectAllImages,
|
||||
getDeviceAccountSettings,
|
||||
maybeGetDevice,
|
||||
maybeGetTimeSettings,
|
||||
} from "../resources/selectors";
|
||||
import {
|
||||
sourceFbosConfigValue, sourceFwConfigValue
|
||||
} from "./components/source_config_value";
|
||||
import { validFwConfig, validFbosConfig } from "../util";
|
||||
import {
|
||||
determineInstalledOsVersion, validFwConfig, validFbosConfig,
|
||||
createShouldDisplayFn as shouldDisplayFunc
|
||||
} from "../util";
|
||||
import {
|
||||
saveOrEditFarmwareEnv, reduceFarmwareEnv
|
||||
saveOrEditFarmwareEnv, getEnv, getShouldDisplayFn
|
||||
} from "../farmware/state_to_props";
|
||||
import { getFbosConfig, getFirmwareConfig, getWebAppConfig } from "../resources/getters";
|
||||
import { DevSettings } from "../account/dev/dev_support";
|
||||
import {
|
||||
getFbosConfig, getFirmwareConfig, getWebAppConfig
|
||||
} from "../resources/getters";
|
||||
import { getAllAlerts } from "../messages/state_to_props";
|
||||
|
||||
export function mapStateToProps(props: Everything): Props {
|
||||
const { hardware } = props.bot;
|
||||
const fbosConfig = validFbosConfig(getFbosConfig(props.resources.index));
|
||||
const firmwareConfig = validFwConfig(getFirmwareConfig(props.resources.index));
|
||||
const installedOsVersion = determineInstalledOsVersion(
|
||||
props.bot, maybeGetDevice(props.resources.index));
|
||||
const fbosVersionOverride = DevSettings.overriddenFbosVersion();
|
||||
const shouldDisplay = shouldDisplayFunc(installedOsVersion,
|
||||
props.bot.minOsFeatureData,
|
||||
fbosVersionOverride);
|
||||
const env = shouldDisplay(Feature.api_farmware_env)
|
||||
? reduceFarmwareEnv(props.resources.index)
|
||||
: props.bot.hardware.user_env;
|
||||
const shouldDisplay = getShouldDisplayFn(props.resources.index, props.bot);
|
||||
const env = getEnv(props.resources.index, shouldDisplay, props.bot);
|
||||
const webAppConfig = getWebAppConfig(props.resources.index);
|
||||
if (!webAppConfig) {
|
||||
throw new Error("Missing web app config");
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
findSequenceById,
|
||||
findRegimenById,
|
||||
getDeviceAccountSettings,
|
||||
maybeGetDevice,
|
||||
maybeGetTimeSettings
|
||||
} from "../../resources/selectors";
|
||||
import {
|
||||
|
@ -22,11 +21,7 @@ import {
|
|||
TaggedRegimen
|
||||
} from "farmbot";
|
||||
import { DropDownItem } from "../../ui/index";
|
||||
import {
|
||||
validFbosConfig,
|
||||
createShouldDisplayFn as shouldDisplayFunc,
|
||||
determineInstalledOsVersion
|
||||
} from "../../util";
|
||||
import { validFbosConfig } from "../../util";
|
||||
import {
|
||||
sourceFbosConfigValue
|
||||
} from "../../devices/components/source_config_value";
|
||||
|
@ -34,7 +29,7 @@ import { hasId } from "../../resources/util";
|
|||
import { ExecutableType } from "farmbot/dist/resources/api_resources";
|
||||
import { getFbosConfig } from "../../resources/getters";
|
||||
import { t } from "../../i18next_wrapper";
|
||||
import { DevSettings } from "../../account/dev/dev_support";
|
||||
import { getShouldDisplayFn } from "../../farmware/state_to_props";
|
||||
|
||||
export const formatTime = (input: string, timeSettings: TimeSettings) => {
|
||||
const iso = new Date(input).toISOString();
|
||||
|
@ -143,12 +138,6 @@ export function mapStateToPropsAddEdit(props: Everything): AddEditFarmEventProps
|
|||
const autoSyncEnabled =
|
||||
!!sourceFbosConfigValue(fbosConfig, configuration)("auto_sync").value;
|
||||
|
||||
const installedOsVersion = determineInstalledOsVersion(
|
||||
props.bot, maybeGetDevice(props.resources.index));
|
||||
const fbosVersionOverride = DevSettings.overriddenFbosVersion();
|
||||
const shouldDisplay = shouldDisplayFunc(
|
||||
installedOsVersion, props.bot.minOsFeatureData, fbosVersionOverride);
|
||||
|
||||
return {
|
||||
deviceTimezone: dev
|
||||
.body
|
||||
|
@ -167,6 +156,6 @@ export function mapStateToPropsAddEdit(props: Everything): AddEditFarmEventProps
|
|||
timeSettings: maybeGetTimeSettings(props.resources.index),
|
||||
autoSyncEnabled,
|
||||
resources: props.resources.index,
|
||||
shouldDisplay,
|
||||
shouldDisplay: getShouldDisplayFn(props.resources.index, props.bot),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,24 +9,17 @@ import {
|
|||
selectAllPlantTemplates,
|
||||
selectAllSensorReadings,
|
||||
selectAllSensors,
|
||||
maybeGetDevice,
|
||||
maybeGetTimeSettings
|
||||
} from "../resources/selectors";
|
||||
import {
|
||||
validBotLocationData, validFwConfig, unpackUUID,
|
||||
createShouldDisplayFn as shouldDisplayFunc,
|
||||
determineInstalledOsVersion
|
||||
} from "../util";
|
||||
import { validBotLocationData, validFwConfig, unpackUUID } from "../util";
|
||||
import { getWebAppConfigValue } from "../config_storage/actions";
|
||||
import { Props } from "./interfaces";
|
||||
import { TaggedPlant } from "./map/interfaces";
|
||||
import { RestResources } from "../resources/interfaces";
|
||||
import { isString, uniq, chain } from "lodash";
|
||||
import { BooleanSetting } from "../session_keys";
|
||||
import { Feature } from "../devices/interfaces";
|
||||
import { reduceFarmwareEnv } from "../farmware/state_to_props";
|
||||
import { getEnv, getShouldDisplayFn } from "../farmware/state_to_props";
|
||||
import { getFirmwareConfig } from "../resources/getters";
|
||||
import { DevSettings } from "../account/dev/dev_support";
|
||||
import { calcMicrostepsPerMm } from "../controls/move/direction_axes_props";
|
||||
|
||||
const plantFinder = (plants: TaggedPlant[]) =>
|
||||
|
@ -84,14 +77,8 @@ export function mapStateToProps(props: Everything): Props {
|
|||
.reverse()
|
||||
.value();
|
||||
|
||||
const installedOsVersion = determineInstalledOsVersion(
|
||||
props.bot, maybeGetDevice(props.resources.index));
|
||||
const fbosVersionOverride = DevSettings.overriddenFbosVersion();
|
||||
const shouldDisplay = shouldDisplayFunc(
|
||||
installedOsVersion, props.bot.minOsFeatureData, fbosVersionOverride);
|
||||
const env = shouldDisplay(Feature.api_farmware_env)
|
||||
? reduceFarmwareEnv(props.resources.index)
|
||||
: props.bot.hardware.user_env;
|
||||
const shouldDisplay = getShouldDisplayFn(props.resources.index, props.bot);
|
||||
const env = getEnv(props.resources.index, shouldDisplay, props.bot);
|
||||
|
||||
const cameraCalibrationData = {
|
||||
scale: env["CAMERA_CALIBRATION_coord_scale"],
|
||||
|
|
|
@ -93,7 +93,7 @@ describe("<FarmwareForm />", () => {
|
|||
const fakeProps = (): FarmwareFormProps => {
|
||||
return {
|
||||
farmware: fakeFarmware(),
|
||||
user_env: {},
|
||||
env: {},
|
||||
dispatch: jest.fn(),
|
||||
shouldDisplay: () => false,
|
||||
saveFarmwareEnv: jest.fn(),
|
||||
|
|
|
@ -16,8 +16,8 @@ describe("<FarmwarePage />", () => {
|
|||
const fakeProps = (): FarmwareProps => ({
|
||||
farmwares: fakeFarmwares(),
|
||||
botToMqttStatus: "up",
|
||||
wDEnv: {},
|
||||
env: {},
|
||||
user_env: {},
|
||||
dispatch: jest.fn(),
|
||||
currentImage: undefined,
|
||||
images: [],
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
let mockLastUrlChunk = "farmware";
|
||||
jest.mock("../../util/urls", () => ({
|
||||
urlFriendly: jest.fn(x => x),
|
||||
lastUrlChunk: jest.fn(() => mockLastUrlChunk)
|
||||
}));
|
||||
|
||||
jest.mock("../../util/urls", () => {
|
||||
return {
|
||||
urlFriendly: jest.fn(x => x),
|
||||
lastUrlChunk: jest.fn(() => mockLastUrlChunk)
|
||||
};
|
||||
});
|
||||
jest.mock("../../redux/store", () => ({ store: { dispatch: jest.fn() } }));
|
||||
|
||||
jest.mock("../../redux/store", () => ({
|
||||
store: { dispatch: jest.fn() }
|
||||
jest.mock("../../account/dev/dev_support", () => ({
|
||||
DevSettings: {
|
||||
futureFeaturesEnabled: () => false,
|
||||
}
|
||||
}));
|
||||
|
||||
import { setActiveFarmwareByName } from "../set_active_farmware_by_name";
|
||||
|
|
|
@ -43,7 +43,7 @@ describe("mapStateToProps()", () => {
|
|||
const state = fakeState();
|
||||
state.bot.hardware.user_env = env;
|
||||
const props = mapStateToProps(state);
|
||||
expect(props.user_env).toEqual(env);
|
||||
expect(props.env).toEqual(env);
|
||||
});
|
||||
|
||||
it("returns API farmware env", () => {
|
||||
|
@ -53,7 +53,7 @@ describe("mapStateToProps()", () => {
|
|||
DevSettings.MAX_FBOS_VERSION_OVERRIDE;
|
||||
state.resources = buildResourceIndex([fakeFarmwareEnv()]);
|
||||
const props = mapStateToProps(state);
|
||||
expect(props.user_env).toEqual({
|
||||
expect(props.env).toEqual({
|
||||
fake_FarmwareEnv_key: "fake_FarmwareEnv_value"
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
const mockDevice = { setUserEnv: jest.fn(() => Promise.resolve({})) };
|
||||
jest.mock("../../../device", () => ({ getDevice: () => mockDevice }));
|
||||
|
||||
jest.mock("../actions", () => ({ scanImage: jest.fn() }));
|
||||
jest.mock("../../images/actions", () => ({ selectImage: jest.fn() }));
|
||||
|
||||
|
@ -11,12 +10,15 @@ import { CameraCalibrationProps } from "../interfaces";
|
|||
import { scanImage } from "../actions";
|
||||
import { selectImage } from "../../images/actions";
|
||||
import { fakeTimeSettings } from "../../../__test_support__/fake_time_settings";
|
||||
import { error } from "../../../toast/toast";
|
||||
import { Content, ToolTips } from "../../../constants";
|
||||
|
||||
describe("<CameraCalibration/>", () => {
|
||||
const fakeProps = (): CameraCalibrationProps => ({
|
||||
dispatch: jest.fn(),
|
||||
currentImage: undefined,
|
||||
images: [],
|
||||
wDEnv: {},
|
||||
env: {},
|
||||
iteration: 1,
|
||||
morph: 2,
|
||||
|
@ -95,4 +97,23 @@ describe("<CameraCalibration/>", () => {
|
|||
expect(p.saveFarmwareEnv).toHaveBeenCalledWith(
|
||||
"CAMERA_CALIBRATION_image_bot_origin_location", "\"BOTTOM_LEFT\"");
|
||||
});
|
||||
|
||||
it("shows calibrate as enabled", () => {
|
||||
const wrapper = shallow(<CameraCalibration {...fakeProps()} />);
|
||||
const btn = wrapper.find("button").first();
|
||||
expect(btn.text()).toEqual("Calibrate");
|
||||
expect(btn.props().title).not.toEqual(Content.NO_CAMERA_SELECTED);
|
||||
expect(error).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows calibrate as disabled when camera is disabled", () => {
|
||||
const p = fakeProps();
|
||||
p.env = { camera: "NONE" };
|
||||
const wrapper = shallow(<CameraCalibration {...p} />);
|
||||
const btn = wrapper.find("button").first();
|
||||
expect(btn.props().title).toEqual(Content.NO_CAMERA_SELECTED);
|
||||
btn.simulate("click");
|
||||
expect(error).toHaveBeenCalledWith(
|
||||
ToolTips.SELECT_A_CAMERA, Content.NO_CAMERA_SELECTED);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,6 +13,9 @@ import { Feature } from "../../devices/interfaces";
|
|||
import { namespace } from "../weed_detector";
|
||||
import { t } from "../../i18next_wrapper";
|
||||
import { formatEnvKey } from "../weed_detector/remote_env/translators";
|
||||
import {
|
||||
cameraBtnProps
|
||||
} from "../../devices/components/fbos_settings/camera_selection";
|
||||
|
||||
export class CameraCalibration extends
|
||||
React.Component<CameraCalibrationProps, {}> {
|
||||
|
@ -29,6 +32,7 @@ export class CameraCalibration extends
|
|||
: envSave(key, value)
|
||||
|
||||
render() {
|
||||
const camDisabled = cameraBtnProps(this.props.env);
|
||||
return <div className="weed-detector">
|
||||
<div className="farmware-button">
|
||||
<MustBeOnline
|
||||
|
@ -37,8 +41,9 @@ export class CameraCalibration extends
|
|||
hideBanner={true}
|
||||
lockOpen={process.env.NODE_ENV !== "production"}>
|
||||
<button
|
||||
onClick={this.props.dispatch(calibrate)}
|
||||
className="fb-button green">
|
||||
className={`fb-button green ${camDisabled.class}`}
|
||||
title={camDisabled.title}
|
||||
onClick={camDisabled.click || this.props.dispatch(calibrate)}>
|
||||
{t("Calibrate")}
|
||||
</button>
|
||||
</MustBeOnline>
|
||||
|
@ -68,9 +73,9 @@ export class CameraCalibration extends
|
|||
S_HI={this.props.S_HI}
|
||||
V_HI={this.props.V_HI}
|
||||
invertHue={!!envGet(this.namespace("invert_hue_selection"),
|
||||
this.props.env)} />
|
||||
this.props.wDEnv)} />
|
||||
<WeedDetectorConfig
|
||||
values={this.props.env}
|
||||
values={this.props.wDEnv}
|
||||
onChange={this.saveEnvVar} />
|
||||
</MustBeOnline>
|
||||
</Col>
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
import { TaggedImage, SyncStatus } from "farmbot";
|
||||
import { WD_ENV } from "../weed_detector/remote_env/interfaces";
|
||||
import { NetworkState } from "../../connectivity/interfaces";
|
||||
import { ShouldDisplay, SaveFarmwareEnv } from "../../devices/interfaces";
|
||||
import {
|
||||
ShouldDisplay, SaveFarmwareEnv, UserEnv
|
||||
} from "../../devices/interfaces";
|
||||
import { TimeSettings } from "../../interfaces";
|
||||
|
||||
export interface CameraCalibrationProps {
|
||||
dispatch: Function;
|
||||
images: TaggedImage[];
|
||||
currentImage: TaggedImage | undefined;
|
||||
env: Partial<WD_ENV>;
|
||||
wDEnv: Partial<WD_ENV>;
|
||||
env: UserEnv;
|
||||
iteration: number;
|
||||
morph: number;
|
||||
blur: number;
|
||||
|
|
|
@ -11,7 +11,7 @@ import { t } from "../i18next_wrapper";
|
|||
|
||||
export interface FarmwareFormProps {
|
||||
farmware: FarmwareManifestInfo;
|
||||
user_env: UserEnv;
|
||||
env: UserEnv;
|
||||
shouldDisplay: ShouldDisplay;
|
||||
saveFarmwareEnv: SaveFarmwareEnv;
|
||||
dispatch: Function;
|
||||
|
@ -73,7 +73,7 @@ export function FarmwareForm(props: FarmwareFormProps): JSX.Element {
|
|||
|
||||
/** Get a Farmware input value from FBOS. */
|
||||
function getValue(farmwareName: string, currentConfig: FarmwareConfig) {
|
||||
return (user_env[getConfigEnvName(farmwareName, currentConfig.name)]
|
||||
return (env[getConfigEnvName(farmwareName, currentConfig.name)]
|
||||
|| toString(currentConfig.value));
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ export function FarmwareForm(props: FarmwareFormProps): JSX.Element {
|
|||
getDevice().execScript(farmwareName, pairs).catch(() => { });
|
||||
}
|
||||
|
||||
const { farmware, user_env } = props;
|
||||
const { farmware, env } = props;
|
||||
return <Col key={farmware.name}>
|
||||
<div className={kebabCase(farmware.name)}>
|
||||
<button
|
||||
|
|
|
@ -26,8 +26,8 @@ describe("<ImageFlipper/>", () => {
|
|||
|
||||
it("defaults to index 0 and flips up", () => {
|
||||
const p = fakeProps();
|
||||
const x = shallow(<ImageFlipper {...p} />);
|
||||
const up = (x.instance() as ImageFlipper).go(1);
|
||||
const flipper = shallow<ImageFlipper>(<ImageFlipper {...p} />);
|
||||
const up = flipper.instance().go(1);
|
||||
up();
|
||||
expect(p.onFlip).toHaveBeenCalledWith(p.images[1].uuid);
|
||||
});
|
||||
|
@ -35,8 +35,8 @@ describe("<ImageFlipper/>", () => {
|
|||
it("flips down", () => {
|
||||
const p = fakeProps();
|
||||
p.currentImage = p.images[1];
|
||||
const x = shallow(<ImageFlipper {...p} />);
|
||||
const down = (x.instance() as ImageFlipper).go(-1);
|
||||
const flipper = shallow<ImageFlipper>(<ImageFlipper {...p} />);
|
||||
const down = flipper.instance().go(-1);
|
||||
down();
|
||||
expect(p.onFlip).toHaveBeenCalledWith(p.images[0].uuid);
|
||||
});
|
||||
|
@ -44,8 +44,8 @@ describe("<ImageFlipper/>", () => {
|
|||
it("stops at upper end", () => {
|
||||
const p = fakeProps();
|
||||
p.currentImage = p.images[2];
|
||||
const x = shallow(<ImageFlipper {...p} />);
|
||||
const up = (x.instance() as ImageFlipper).go(1);
|
||||
const flipper = shallow<ImageFlipper>(<ImageFlipper {...p} />);
|
||||
const up = flipper.instance().go(1);
|
||||
up();
|
||||
expect(p.onFlip).not.toHaveBeenCalled();
|
||||
});
|
||||
|
@ -53,8 +53,8 @@ describe("<ImageFlipper/>", () => {
|
|||
it("stops at lower end", () => {
|
||||
const p = fakeProps();
|
||||
p.currentImage = p.images[0];
|
||||
const x = shallow(<ImageFlipper {...p} />);
|
||||
const down = (x.instance() as ImageFlipper).go(-1);
|
||||
const flipper = shallow<ImageFlipper>(<ImageFlipper {...p} />);
|
||||
const down = flipper.instance().go(-1);
|
||||
down();
|
||||
expect(p.onFlip).not.toHaveBeenCalled();
|
||||
});
|
||||
|
@ -118,4 +118,12 @@ describe("<ImageFlipper/>", () => {
|
|||
const wrapper = mount(<ImageFlipper {...p} />);
|
||||
expect(wrapper.find("img").last().props().src).toEqual(PLACEHOLDER_FARMBOT);
|
||||
});
|
||||
|
||||
it("knows when image is loaded", () => {
|
||||
const wrapper = mount<ImageFlipper>(<ImageFlipper {...fakeProps()} />);
|
||||
const image = shallow(wrapper.instance().imageJSX());
|
||||
expect(wrapper.state().isLoaded).toEqual(false);
|
||||
image.find("img").simulate("load");
|
||||
expect(wrapper.state().isLoaded).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,6 +16,7 @@ import { PhotosProps } from "../interfaces";
|
|||
import { selectImage } from "../actions";
|
||||
import { fakeTimeSettings } from "../../../__test_support__/fake_time_settings";
|
||||
import { success, error } from "../../../toast/toast";
|
||||
import { Content, ToolTips } from "../../../constants";
|
||||
|
||||
describe("<Photos/>", () => {
|
||||
const fakeProps = (): PhotosProps => ({
|
||||
|
@ -26,6 +27,7 @@ describe("<Photos/>", () => {
|
|||
imageJobs: [],
|
||||
botToMqttStatus: "up",
|
||||
syncStatus: "synced",
|
||||
env: {},
|
||||
});
|
||||
|
||||
it("shows photo", () => {
|
||||
|
@ -44,9 +46,25 @@ describe("<Photos/>", () => {
|
|||
|
||||
it("takes photo", async () => {
|
||||
const wrapper = mount(<Photos {...fakeProps()} />);
|
||||
const btn = wrapper.find("button").first();
|
||||
expect(btn.props().title).not.toEqual(Content.NO_CAMERA_SELECTED);
|
||||
await clickButton(wrapper, 0, "take photo");
|
||||
expect(mockDevice.takePhoto).toHaveBeenCalled();
|
||||
await expect(success).toHaveBeenCalled();
|
||||
expect(error).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows disabled take photo button", () => {
|
||||
const p = fakeProps();
|
||||
p.env = { camera: "NONE" };
|
||||
const wrapper = mount(<Photos {...p} />);
|
||||
const btn = wrapper.find("button").first();
|
||||
expect(btn.text()).toEqual("Take Photo");
|
||||
expect(btn.props().title).toEqual(Content.NO_CAMERA_SELECTED);
|
||||
btn.simulate("click");
|
||||
expect(error).toHaveBeenCalledWith(
|
||||
ToolTips.SELECT_A_CAMERA, Content.NO_CAMERA_SELECTED);
|
||||
expect(mockDevice.takePhoto).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("fails to take photo", async () => {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { TaggedImage, JobProgress, SyncStatus } from "farmbot";
|
||||
import { NetworkState } from "../../connectivity/interfaces";
|
||||
import { TimeSettings } from "../../interfaces";
|
||||
import { UserEnv } from "../../devices/interfaces";
|
||||
|
||||
export interface ImageFlipperProps {
|
||||
onFlip(uuid: string | undefined): void;
|
||||
|
@ -22,6 +23,7 @@ export interface PhotosProps {
|
|||
imageJobs: JobProgress[];
|
||||
botToMqttStatus: NetworkState;
|
||||
syncStatus: SyncStatus | undefined;
|
||||
env: UserEnv;
|
||||
}
|
||||
|
||||
export interface PhotoButtonsProps {
|
||||
|
@ -30,4 +32,5 @@ export interface PhotoButtonsProps {
|
|||
imageJobs: JobProgress[],
|
||||
botToMqttStatus: NetworkState;
|
||||
syncStatus: SyncStatus | undefined;
|
||||
env: UserEnv;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@ import { startCase } from "lodash";
|
|||
import { MustBeOnline } from "../../devices/must_be_online";
|
||||
import { t } from "../../i18next_wrapper";
|
||||
import { TimeSettings } from "../../interfaces";
|
||||
import {
|
||||
cameraBtnProps
|
||||
} from "../../devices/components/fbos_settings/camera_selection";
|
||||
|
||||
interface MetaInfoProps {
|
||||
/** Default conversion is `attr_name ==> Attr Name`.
|
||||
|
@ -52,6 +55,7 @@ const PhotoMetaData = ({ image }: { image: TaggedImage | undefined }) =>
|
|||
|
||||
const PhotoButtons = (props: PhotoButtonsProps) => {
|
||||
const imageUploadJobProgress = downloadProgress(props.imageJobs[0]);
|
||||
const camDisabled = cameraBtnProps(props.env);
|
||||
return <div className="farmware-button">
|
||||
<MustBeOnline
|
||||
syncStatus={props.syncStatus}
|
||||
|
@ -59,8 +63,9 @@ const PhotoButtons = (props: PhotoButtonsProps) => {
|
|||
hideBanner={true}
|
||||
lockOpen={process.env.NODE_ENV !== "production"}>
|
||||
<button
|
||||
className="fb-button green"
|
||||
onClick={props.takePhoto}>
|
||||
className={`fb-button green ${camDisabled.class}`}
|
||||
title={camDisabled.title}
|
||||
onClick={camDisabled.click || props.takePhoto}>
|
||||
{t("Take Photo")}
|
||||
</button>
|
||||
</MustBeOnline>
|
||||
|
@ -125,6 +130,7 @@ export class Photos extends React.Component<PhotosProps, {}> {
|
|||
botToMqttStatus={this.props.botToMqttStatus}
|
||||
takePhoto={this.takePhoto}
|
||||
deletePhoto={this.deletePhoto}
|
||||
env={this.props.env}
|
||||
imageJobs={this.props.imageJobs} />
|
||||
<ImageFlipper
|
||||
onFlip={id => this.props.dispatch(selectImage(id))}
|
||||
|
|
|
@ -24,6 +24,7 @@ import { t } from "../i18next_wrapper";
|
|||
import { isBotOnline } from "../devices/must_be_online";
|
||||
import { BooleanSetting } from "../session_keys";
|
||||
import { Dictionary } from "farmbot";
|
||||
import { WDENVKey } from "./weed_detector/remote_env/interfaces";
|
||||
|
||||
/** Get the correct help text for the provided Farmware. */
|
||||
const getToolTipByFarmware =
|
||||
|
@ -136,6 +137,7 @@ export class RawFarmwarePage extends React.Component<FarmwareProps, {}> {
|
|||
|
||||
/** Load Farmware input panel contents for 1st & 3rd party Farmware. */
|
||||
getPanelByFarmware(farmwareName: string) {
|
||||
const wDEnvGet = (key: WDENVKey) => envGet(key, this.props.wDEnv);
|
||||
switch (farmwareUrlFriendly(farmwareName)) {
|
||||
case "take_photo":
|
||||
case "photos":
|
||||
|
@ -145,6 +147,7 @@ export class RawFarmwarePage extends React.Component<FarmwareProps, {}> {
|
|||
timeSettings={this.props.timeSettings}
|
||||
dispatch={this.props.dispatch}
|
||||
images={this.props.images}
|
||||
env={this.props.env}
|
||||
currentImage={this.props.currentImage}
|
||||
imageJobs={this.props.imageJobs} />;
|
||||
case "camera_calibration":
|
||||
|
@ -153,17 +156,18 @@ export class RawFarmwarePage extends React.Component<FarmwareProps, {}> {
|
|||
dispatch={this.props.dispatch}
|
||||
currentImage={this.props.currentImage}
|
||||
images={this.props.images}
|
||||
wDEnv={this.props.wDEnv}
|
||||
env={this.props.env}
|
||||
saveFarmwareEnv={this.props.saveFarmwareEnv}
|
||||
iteration={envGet("CAMERA_CALIBRATION_iteration", this.props.env)}
|
||||
morph={envGet("CAMERA_CALIBRATION_morph", this.props.env)}
|
||||
blur={envGet("CAMERA_CALIBRATION_blur", this.props.env)}
|
||||
H_LO={envGet("CAMERA_CALIBRATION_H_LO", this.props.env)}
|
||||
S_LO={envGet("CAMERA_CALIBRATION_S_LO", this.props.env)}
|
||||
V_LO={envGet("CAMERA_CALIBRATION_V_LO", this.props.env)}
|
||||
H_HI={envGet("CAMERA_CALIBRATION_H_HI", this.props.env)}
|
||||
S_HI={envGet("CAMERA_CALIBRATION_S_HI", this.props.env)}
|
||||
V_HI={envGet("CAMERA_CALIBRATION_V_HI", this.props.env)}
|
||||
iteration={wDEnvGet("CAMERA_CALIBRATION_iteration")}
|
||||
morph={wDEnvGet("CAMERA_CALIBRATION_morph")}
|
||||
blur={wDEnvGet("CAMERA_CALIBRATION_blur")}
|
||||
H_LO={wDEnvGet("CAMERA_CALIBRATION_H_LO")}
|
||||
S_LO={wDEnvGet("CAMERA_CALIBRATION_S_LO")}
|
||||
V_LO={wDEnvGet("CAMERA_CALIBRATION_V_LO")}
|
||||
H_HI={wDEnvGet("CAMERA_CALIBRATION_H_HI")}
|
||||
S_HI={wDEnvGet("CAMERA_CALIBRATION_S_HI")}
|
||||
V_HI={wDEnvGet("CAMERA_CALIBRATION_V_HI")}
|
||||
timeSettings={this.props.timeSettings}
|
||||
shouldDisplay={this.props.shouldDisplay}
|
||||
botToMqttStatus={this.props.botToMqttStatus} />;
|
||||
|
@ -174,7 +178,7 @@ export class RawFarmwarePage extends React.Component<FarmwareProps, {}> {
|
|||
const farmware = getFarmwareByName(this.props.farmwares, farmwareName);
|
||||
return farmware && needsFarmwareForm(farmware)
|
||||
? <FarmwareForm farmware={farmware}
|
||||
user_env={this.props.user_env}
|
||||
env={this.props.env}
|
||||
shouldDisplay={this.props.shouldDisplay}
|
||||
saveFarmwareEnv={this.props.saveFarmwareEnv}
|
||||
botOnline={this.botOnline}
|
||||
|
|
|
@ -3,7 +3,7 @@ import {
|
|||
selectAllImages, maybeGetDevice, maybeGetTimeSettings
|
||||
} from "../resources/selectors";
|
||||
import {
|
||||
FarmwareProps, Feature, SaveFarmwareEnv, UserEnv
|
||||
FarmwareProps, Feature, SaveFarmwareEnv, UserEnv, ShouldDisplay, BotState
|
||||
} from "../devices/interfaces";
|
||||
import { prepopulateEnv } from "./weed_detector/remote_env/selectors";
|
||||
import {
|
||||
|
@ -11,7 +11,7 @@ import {
|
|||
} from "../resources/selectors_by_kind";
|
||||
import {
|
||||
determineInstalledOsVersion,
|
||||
createShouldDisplayFn as shouldDisplayFunc,
|
||||
createShouldDisplayFn,
|
||||
betterCompact
|
||||
} from "../util";
|
||||
import { ResourceIndex } from "../resources/interfaces";
|
||||
|
@ -51,6 +51,20 @@ export const reduceFarmwareEnv =
|
|||
return farmwareEnv;
|
||||
};
|
||||
|
||||
export const getEnv =
|
||||
(ri: ResourceIndex, shouldDisplay: ShouldDisplay, bot: BotState) =>
|
||||
shouldDisplay(Feature.api_farmware_env)
|
||||
? reduceFarmwareEnv(ri)
|
||||
: bot.hardware.user_env;
|
||||
|
||||
export const getShouldDisplayFn = (ri: ResourceIndex, bot: BotState) => {
|
||||
const lookupData = bot.minOsFeatureData;
|
||||
const installed = determineInstalledOsVersion(bot, maybeGetDevice(ri));
|
||||
const override = DevSettings.overriddenFbosVersion();
|
||||
const shouldDisplay = createShouldDisplayFn(installed, lookupData, override);
|
||||
return shouldDisplay;
|
||||
};
|
||||
|
||||
export function mapStateToProps(props: Everything): FarmwareProps {
|
||||
const images = chain(selectAllImages(props.resources.index))
|
||||
.sortBy(x => x.body.id)
|
||||
|
@ -64,14 +78,8 @@ export function mapStateToProps(props: Everything): FarmwareProps {
|
|||
const { currentFarmware, firstPartyFarmwareNames, infoOpen } =
|
||||
props.resources.consumers.farmware;
|
||||
|
||||
const installedOsVersion = determineInstalledOsVersion(
|
||||
props.bot, maybeGetDevice(props.resources.index));
|
||||
const fbosVersionOverride = DevSettings.overriddenFbosVersion();
|
||||
const shouldDisplay = shouldDisplayFunc(
|
||||
installedOsVersion, props.bot.minOsFeatureData, fbosVersionOverride);
|
||||
const env = shouldDisplay(Feature.api_farmware_env)
|
||||
? reduceFarmwareEnv(props.resources.index)
|
||||
: props.bot.hardware.user_env;
|
||||
const shouldDisplay = getShouldDisplayFn(props.resources.index, props.bot);
|
||||
const env = getEnv(props.resources.index, shouldDisplay, props.bot);
|
||||
|
||||
const taggedFarmwareInstallations =
|
||||
selectAllFarmwareInstallations(props.resources.index);
|
||||
|
@ -115,8 +123,8 @@ export function mapStateToProps(props: Everything): FarmwareProps {
|
|||
currentFarmware,
|
||||
farmwares,
|
||||
botToMqttStatus,
|
||||
env: prepopulateEnv(env),
|
||||
user_env: env,
|
||||
wDEnv: prepopulateEnv(env),
|
||||
env,
|
||||
dispatch: props.dispatch,
|
||||
currentImage,
|
||||
images,
|
||||
|
|
|
@ -23,7 +23,7 @@ jest.mock("../../../util", () => ({
|
|||
}));
|
||||
|
||||
import { deletePoints } from "../actions";
|
||||
import { scanImage, test } from "../actions";
|
||||
import { scanImage, detectPlants } from "../actions";
|
||||
import axios from "axios";
|
||||
import { API } from "../../../api";
|
||||
import { times } from "lodash";
|
||||
|
@ -44,10 +44,10 @@ describe("scanImage()", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("test()", () => {
|
||||
describe("detectPlants()", () => {
|
||||
it("calls out to the device", () => {
|
||||
// Run function to invoke side effects
|
||||
const thunk = test();
|
||||
const thunk = detectPlants();
|
||||
thunk();
|
||||
// Ensure the side effects were the ones we expected.
|
||||
expect(mockDevice.execScript)
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
import * as React from "react";
|
||||
import { mount, shallow } from "enzyme";
|
||||
import { WeedDetectorConfig } from "../config";
|
||||
import { SettingsMenuProps } from "../interfaces";
|
||||
|
||||
describe("<WeedDetectorConfig />", () => {
|
||||
const fakeProps = (): SettingsMenuProps => ({
|
||||
values: {},
|
||||
onChange: jest.fn(),
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
const wrapper = mount(<WeedDetectorConfig
|
||||
values={{}} onChange={jest.fn()} />);
|
||||
const wrapper = mount(<WeedDetectorConfig {...fakeProps()} />);
|
||||
["Invert Hue Range Selection",
|
||||
"Calibration Object Separation",
|
||||
"Calibration Object Separation along axis",
|
||||
|
@ -15,15 +20,39 @@ describe("<WeedDetectorConfig />", () => {
|
|||
.map(string => expect(wrapper.text()).toContain(string));
|
||||
});
|
||||
|
||||
it("changes value", () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = shallow(<WeedDetectorConfig
|
||||
values={{}} onChange={onChange} />);
|
||||
it("changes axis value", () => {
|
||||
const p = fakeProps();
|
||||
const wrapper = shallow(<WeedDetectorConfig {...p} />);
|
||||
const input = wrapper.find("FBSelect").first();
|
||||
input.simulate("change", { label: "", value: 4 });
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect(p.onChange).toHaveBeenCalledWith(
|
||||
"CAMERA_CALIBRATION_calibration_along_axis", 4);
|
||||
const badChange = () => input.simulate("change", { label: "", value: "4" });
|
||||
expect(badChange).toThrow("Weed detector got a non-numeric value");
|
||||
});
|
||||
|
||||
it("changes hue invert value", () => {
|
||||
const p = fakeProps();
|
||||
const wrapper = shallow(<WeedDetectorConfig {...p} />);
|
||||
const input = wrapper.find("input").first();
|
||||
input.simulate("change", { currentTarget: { checked: true } });
|
||||
expect(p.onChange).toHaveBeenCalledWith(
|
||||
"CAMERA_CALIBRATION_invert_hue_selection", 1);
|
||||
input.simulate("change", { currentTarget: { checked: false } });
|
||||
expect(p.onChange).toHaveBeenCalledWith(
|
||||
"CAMERA_CALIBRATION_invert_hue_selection", 0);
|
||||
});
|
||||
|
||||
it("changes number value", () => {
|
||||
const p = fakeProps();
|
||||
const wrapper = shallow<WeedDetectorConfig>(<WeedDetectorConfig {...p} />);
|
||||
const numBox = wrapper.instance().NumberBox({
|
||||
conf: "CAMERA_CALIBRATION_blur", label: "label"
|
||||
});
|
||||
const NumBox = shallow(numBox);
|
||||
NumBox.find("BlurableInput").first().simulate("commit", {
|
||||
currentTarget: { value: "1.23" }
|
||||
});
|
||||
expect(p.onChange).toHaveBeenCalledWith("CAMERA_CALIBRATION_blur", 1.23);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
const mockDevice = {
|
||||
execScript: jest.fn(() => Promise.resolve()),
|
||||
setUserEnv: jest.fn(() => Promise.resolve()),
|
||||
};
|
||||
const mockDevice = { setUserEnv: jest.fn(() => Promise.resolve()) };
|
||||
jest.mock("../../../device", () => ({ getDevice: () => mockDevice }));
|
||||
|
||||
jest.mock("../../images/actions", () => ({ selectImage: jest.fn() }));
|
||||
|
||||
const mockDeletePoints = jest.fn();
|
||||
jest.mock("../actions", () => ({
|
||||
deletePoints: mockDeletePoints,
|
||||
scanImage: jest.fn(),
|
||||
detectPlants: jest.fn(),
|
||||
}));
|
||||
|
||||
import * as React from "react";
|
||||
import { mount, shallow } from "enzyme";
|
||||
import { WeedDetector, namespace } from "../index";
|
||||
|
@ -14,6 +18,9 @@ import { API } from "../../../api";
|
|||
import { selectImage } from "../../images/actions";
|
||||
import { clickButton } from "../../../__test_support__/helpers";
|
||||
import { fakeTimeSettings } from "../../../__test_support__/fake_time_settings";
|
||||
import { deletePoints, detectPlants, scanImage } from "../actions";
|
||||
import { error } from "../../../toast/toast";
|
||||
import { Content, ToolTips } from "../../../constants";
|
||||
|
||||
describe("<WeedDetector />", () => {
|
||||
API.setBaseUrl("http://localhost:3000");
|
||||
|
@ -22,8 +29,8 @@ describe("<WeedDetector />", () => {
|
|||
timeSettings: fakeTimeSettings(),
|
||||
farmwares: {},
|
||||
botToMqttStatus: "up",
|
||||
wDEnv: {},
|
||||
env: {},
|
||||
user_env: {},
|
||||
dispatch: jest.fn(),
|
||||
currentImage: undefined,
|
||||
images: [],
|
||||
|
@ -54,16 +61,38 @@ describe("<WeedDetector />", () => {
|
|||
const p = fakeProps();
|
||||
p.dispatch = jest.fn(x => x());
|
||||
const wrapper = shallow(<WeedDetector {...p} />);
|
||||
const btn = wrapper.find("button").first();
|
||||
expect(btn.props().title).not.toEqual(Content.NO_CAMERA_SELECTED);
|
||||
clickButton(wrapper, 0, "detect weeds");
|
||||
expect(mockDevice.execScript).toHaveBeenCalledWith("plant-detection");
|
||||
expect(detectPlants).toHaveBeenCalled();
|
||||
expect(error).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows detection button as disabled when camera is disabled", () => {
|
||||
const p = fakeProps();
|
||||
p.env = { camera: "NONE" };
|
||||
const wrapper = shallow(<WeedDetector {...p} />);
|
||||
const btn = wrapper.find("button").first();
|
||||
expect(btn.props().title).toEqual(Content.NO_CAMERA_SELECTED);
|
||||
btn.simulate("click");
|
||||
expect(error).toHaveBeenCalledWith(
|
||||
ToolTips.SELECT_A_CAMERA, Content.NO_CAMERA_SELECTED);
|
||||
expect(detectPlants).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("executes clear weeds", () => {
|
||||
const wrapper =
|
||||
shallow<WeedDetector>(<WeedDetector {...fakeProps()} />);
|
||||
const wrapper = shallow<WeedDetector>(<WeedDetector {...fakeProps()} />);
|
||||
expect(wrapper.instance().state.deletionProgress).toBeUndefined();
|
||||
clickButton(wrapper, 1, "clear weeds");
|
||||
expect(deletePoints).toHaveBeenCalledWith(
|
||||
"weeds", { created_by: "plant-detection" }, expect.any(Function));
|
||||
expect(wrapper.instance().state.deletionProgress).toEqual("Deleting...");
|
||||
const fakeProgress = { completed: 50, total: 100, isDone: false };
|
||||
mockDeletePoints.mock.calls[0][2](fakeProgress);
|
||||
expect(wrapper.instance().state.deletionProgress).toEqual("50 %");
|
||||
fakeProgress.isDone = true;
|
||||
mockDeletePoints.mock.calls[0][2](fakeProgress);
|
||||
expect(wrapper.instance().state.deletionProgress).toEqual("");
|
||||
});
|
||||
|
||||
it("saves changes", () => {
|
||||
|
@ -85,16 +114,9 @@ describe("<WeedDetector />", () => {
|
|||
});
|
||||
|
||||
it("calls scanImage", () => {
|
||||
const p = fakeProps();
|
||||
p.dispatch = jest.fn(x => x());
|
||||
const wrapper = shallow(<WeedDetector {...p} />);
|
||||
const wrapper = shallow(<WeedDetector {...fakeProps()} />);
|
||||
wrapper.find("ImageWorkspace").simulate("processPhoto", 1);
|
||||
expect(mockDevice.execScript).toHaveBeenCalledWith(
|
||||
"historical-plant-detection",
|
||||
[expect.objectContaining({
|
||||
kind: "pair",
|
||||
args: expect.objectContaining({ value: "1" })
|
||||
})]);
|
||||
expect(scanImage).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it("calls selectImage", () => {
|
||||
|
|
|
@ -69,7 +69,7 @@ export function scanImage(imageId: number) {
|
|||
};
|
||||
}
|
||||
|
||||
export function test() {
|
||||
export function detectPlants() {
|
||||
return function () {
|
||||
getDevice().execScript("plant-detection").catch(() => { });
|
||||
};
|
||||
|
|
|
@ -154,10 +154,9 @@ export class ImageWorkspace extends React.Component<ImageWorkspaceProps, {}> {
|
|||
<Col xs={12}>
|
||||
<button
|
||||
className="green fb-button"
|
||||
title="Scan this image"
|
||||
title={t("Scan this image")}
|
||||
onClick={this.maybeProcessPhoto}
|
||||
disabled={!this.props.botOnline}
|
||||
hidden={!this.props.images.length}>
|
||||
disabled={!this.props.botOnline || !this.props.images.length}>
|
||||
{t("Scan image")}
|
||||
</button>
|
||||
</Col>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from "react";
|
||||
import { DetectorState } from "./interfaces";
|
||||
import { Row, Col } from "../../ui/index";
|
||||
import { deletePoints, scanImage, test } from "./actions";
|
||||
import { deletePoints, scanImage, detectPlants } from "./actions";
|
||||
import { selectImage } from "../images/actions";
|
||||
import { Progress } from "../../util";
|
||||
import { FarmwareProps, Feature } from "../../devices/interfaces";
|
||||
|
@ -11,6 +11,9 @@ import { envGet } from "./remote_env/selectors";
|
|||
import { MustBeOnline, isBotOnline } from "../../devices/must_be_online";
|
||||
import { envSave } from "./remote_env/actions";
|
||||
import { t } from "../../i18next_wrapper";
|
||||
import {
|
||||
cameraBtnProps
|
||||
} from "../../devices/components/fbos_settings/camera_selection";
|
||||
|
||||
export const namespace = (prefix: string) => (key: string): WDENVKey => {
|
||||
const namespacedKey = prefix + key;
|
||||
|
@ -52,6 +55,8 @@ export class WeedDetector
|
|||
: envSave(key, value)
|
||||
|
||||
render() {
|
||||
const wDEnvGet = (key: WDENVKey) => envGet(key, this.props.wDEnv);
|
||||
const camDisabled = cameraBtnProps(this.props.env);
|
||||
return <div className="weed-detector">
|
||||
<div className="farmware-button">
|
||||
<MustBeOnline
|
||||
|
@ -60,8 +65,9 @@ export class WeedDetector
|
|||
hideBanner={true}
|
||||
lockOpen={process.env.NODE_ENV !== "production"}>
|
||||
<button
|
||||
onClick={this.props.dispatch(test)}
|
||||
className="fb-button green">
|
||||
className={`fb-button green ${camDisabled.class}`}
|
||||
title={camDisabled.title}
|
||||
onClick={camDisabled.click || this.props.dispatch(detectPlants)}>
|
||||
{t("detect weeds")}
|
||||
</button>
|
||||
</MustBeOnline>
|
||||
|
@ -86,15 +92,15 @@ export class WeedDetector
|
|||
images={this.props.images}
|
||||
onChange={this.change}
|
||||
timeSettings={this.props.timeSettings}
|
||||
iteration={envGet(this.namespace("iteration"), this.props.env)}
|
||||
morph={envGet(this.namespace("morph"), this.props.env)}
|
||||
blur={envGet(this.namespace("blur"), this.props.env)}
|
||||
H_LO={envGet(this.namespace("H_LO"), this.props.env)}
|
||||
H_HI={envGet(this.namespace("H_HI"), this.props.env)}
|
||||
S_LO={envGet(this.namespace("S_LO"), this.props.env)}
|
||||
S_HI={envGet(this.namespace("S_HI"), this.props.env)}
|
||||
V_LO={envGet(this.namespace("V_LO"), this.props.env)}
|
||||
V_HI={envGet(this.namespace("V_HI"), this.props.env)} />
|
||||
iteration={wDEnvGet(this.namespace("iteration"))}
|
||||
morph={wDEnvGet(this.namespace("morph"))}
|
||||
blur={wDEnvGet(this.namespace("blur"))}
|
||||
H_LO={wDEnvGet(this.namespace("H_LO"))}
|
||||
H_HI={wDEnvGet(this.namespace("H_HI"))}
|
||||
S_LO={wDEnvGet(this.namespace("S_LO"))}
|
||||
S_HI={wDEnvGet(this.namespace("S_HI"))}
|
||||
V_LO={wDEnvGet(this.namespace("V_LO"))}
|
||||
V_HI={wDEnvGet(this.namespace("V_HI"))} />
|
||||
</MustBeOnline>
|
||||
</Col>
|
||||
</Row>
|
||||
|
|
|
@ -1,21 +1,16 @@
|
|||
import { Everything } from "../interfaces";
|
||||
import {
|
||||
selectAllLogs, maybeGetTimeSettings, maybeGetDevice
|
||||
} from "../resources/selectors";
|
||||
import { selectAllLogs, maybeGetTimeSettings } from "../resources/selectors";
|
||||
import { LogsProps } from "./interfaces";
|
||||
import {
|
||||
sourceFbosConfigValue
|
||||
} from "../devices/components/source_config_value";
|
||||
import {
|
||||
validFbosConfig, determineInstalledOsVersion,
|
||||
createShouldDisplayFn as shouldDisplayFunc
|
||||
} from "../util";
|
||||
import { validFbosConfig } from "../util";
|
||||
import { ResourceIndex } from "../resources/interfaces";
|
||||
import { TaggedLog } from "farmbot";
|
||||
import { getWebAppConfigValue } from "../config_storage/actions";
|
||||
import { getFbosConfig } from "../resources/getters";
|
||||
import { chain } from "lodash";
|
||||
import { DevSettings } from "../account/dev/dev_support";
|
||||
import { getShouldDisplayFn } from "../farmware/state_to_props";
|
||||
|
||||
/** Take the specified number of logs after sorting by time created. */
|
||||
export function takeSortedLogs(
|
||||
|
@ -30,19 +25,12 @@ export function takeSortedLogs(
|
|||
export function mapStateToProps(props: Everything): LogsProps {
|
||||
const { hardware } = props.bot;
|
||||
const fbosConfig = validFbosConfig(getFbosConfig(props.resources.index));
|
||||
const sourceFbosConfig =
|
||||
sourceFbosConfigValue(fbosConfig, hardware.configuration);
|
||||
const installedOsVersion = determineInstalledOsVersion(
|
||||
props.bot, maybeGetDevice(props.resources.index));
|
||||
const fbosVersionOverride = DevSettings.overriddenFbosVersion();
|
||||
const shouldDisplay = shouldDisplayFunc(
|
||||
installedOsVersion, props.bot.minOsFeatureData, fbosVersionOverride);
|
||||
return {
|
||||
dispatch: props.dispatch,
|
||||
sourceFbosConfig,
|
||||
sourceFbosConfig: sourceFbosConfigValue(fbosConfig, hardware.configuration),
|
||||
logs: takeSortedLogs(250, props.resources.index),
|
||||
timeSettings: maybeGetTimeSettings(props.resources.index),
|
||||
getConfigValue: getWebAppConfigValue(() => props),
|
||||
shouldDisplay,
|
||||
shouldDisplay: getShouldDisplayFn(props.resources.index, props.bot),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,21 +9,16 @@ import {
|
|||
maybeGetRegimen,
|
||||
findId,
|
||||
findSequence,
|
||||
maybeGetDevice,
|
||||
findSequenceById,
|
||||
maybeGetTimeSettings
|
||||
} from "../resources/selectors";
|
||||
import { TaggedRegimen, TaggedSequence } from "farmbot";
|
||||
import moment from "moment";
|
||||
import { ResourceIndex, UUID, VariableNameSet } from "../resources/interfaces";
|
||||
import {
|
||||
randomColor, determineInstalledOsVersion,
|
||||
createShouldDisplayFn as shouldDisplayFunc,
|
||||
timeFormatString
|
||||
} from "../util";
|
||||
import { randomColor, timeFormatString } from "../util";
|
||||
import { resourceUsageList } from "../resources/in_use";
|
||||
import { groupBy, chain, sortBy } from "lodash";
|
||||
import { DevSettings } from "../account/dev/dev_support";
|
||||
import { getShouldDisplayFn } from "../farmware/state_to_props";
|
||||
|
||||
export function mapStateToProps(props: Everything): Props {
|
||||
const { resources, dispatch, bot } = props;
|
||||
|
@ -36,12 +31,6 @@ export function mapStateToProps(props: Everything): Props {
|
|||
const calendar = current ?
|
||||
generateCalendar(current, index, dispatch, timeSettings) : [];
|
||||
|
||||
const installedOsVersion = determineInstalledOsVersion(
|
||||
props.bot, maybeGetDevice(props.resources.index));
|
||||
const fbosVersionOverride = DevSettings.overriddenFbosVersion();
|
||||
const shouldDisplay = shouldDisplayFunc(
|
||||
installedOsVersion, props.bot.minOsFeatureData, fbosVersionOverride);
|
||||
|
||||
const calledSequences = (): UUID[] => {
|
||||
if (current) {
|
||||
const sequenceIds = current.body.regimen_items.map(x => x.sequence_id);
|
||||
|
@ -70,7 +59,7 @@ export function mapStateToProps(props: Everything): Props {
|
|||
bot,
|
||||
calendar,
|
||||
regimenUsageStats: resourceUsageList(props.resources.index.inUse),
|
||||
shouldDisplay,
|
||||
shouldDisplay: getShouldDisplayFn(props.resources.index, props.bot),
|
||||
schedulerOpen,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -43,8 +43,8 @@ import {
|
|||
import { fakeSequence } from "../../__test_support__/fake_state/resources";
|
||||
import { destroy, save, edit } from "../../api/crud";
|
||||
import {
|
||||
fakeHardwareFlags
|
||||
} from "../../__test_support__/sequence_hardware_settings";
|
||||
fakeHardwareFlags, fakeFarmwareData as fakeFarmwareData
|
||||
} from "../../__test_support__/fake_sequence_step_data";
|
||||
import { SpecialStatus } from "farmbot";
|
||||
import { move, splice } from "../step_tiles";
|
||||
import { copySequence, editCurrentSequence } from "../actions";
|
||||
|
@ -66,12 +66,7 @@ describe("<SequenceEditorMiddleActive/>", () => {
|
|||
resources: buildResourceIndex(FAKE_RESOURCES).index,
|
||||
syncStatus: "synced",
|
||||
hardwareFlags: fakeHardwareFlags(),
|
||||
farmwareInfo: {
|
||||
farmwareNames: [],
|
||||
firstPartyFarmwareNames: [],
|
||||
showFirstPartyFarmware: false,
|
||||
farmwareConfigs: {},
|
||||
},
|
||||
farmwareData: fakeFarmwareData(),
|
||||
shouldDisplay: jest.fn(),
|
||||
getWebAppConfigValue: jest.fn(),
|
||||
menuOpen: false,
|
||||
|
|
|
@ -7,8 +7,8 @@ import {
|
|||
} from "../../__test_support__/resource_index_builder";
|
||||
import { fakeSequence } from "../../__test_support__/fake_state/resources";
|
||||
import {
|
||||
fakeHardwareFlags
|
||||
} from "../../__test_support__/sequence_hardware_settings";
|
||||
fakeHardwareFlags, fakeFarmwareData
|
||||
} from "../../__test_support__/fake_sequence_step_data";
|
||||
|
||||
describe("<SequenceEditorMiddle/>", () => {
|
||||
function fakeProps(): SequenceEditorMiddleProps {
|
||||
|
@ -18,12 +18,7 @@ describe("<SequenceEditorMiddle/>", () => {
|
|||
resources: buildResourceIndex(FAKE_RESOURCES).index,
|
||||
syncStatus: "synced",
|
||||
hardwareFlags: fakeHardwareFlags(),
|
||||
farmwareInfo: {
|
||||
farmwareNames: [],
|
||||
firstPartyFarmwareNames: [],
|
||||
showFirstPartyFarmware: false,
|
||||
farmwareConfigs: {},
|
||||
},
|
||||
farmwareData: fakeFarmwareData(),
|
||||
shouldDisplay: jest.fn(),
|
||||
getWebAppConfigValue: jest.fn(),
|
||||
menuOpen: false,
|
||||
|
|
|
@ -15,8 +15,8 @@ import {
|
|||
import { fakeSequence } from "../../__test_support__/fake_state/resources";
|
||||
import { ToolTips, Actions } from "../../constants";
|
||||
import {
|
||||
fakeHardwareFlags
|
||||
} from "../../__test_support__/sequence_hardware_settings";
|
||||
fakeHardwareFlags, fakeFarmwareData
|
||||
} from "../../__test_support__/fake_sequence_step_data";
|
||||
import { push } from "../../history";
|
||||
import { mapStateToFolderProps } from "../../folders/map_state_to_props";
|
||||
import { fakeState } from "../../__test_support__/fake_state";
|
||||
|
@ -29,12 +29,7 @@ describe("<Sequences/>", () => {
|
|||
resources: buildResourceIndex(FAKE_RESOURCES).index,
|
||||
syncStatus: "synced",
|
||||
hardwareFlags: fakeHardwareFlags(),
|
||||
farmwareInfo: {
|
||||
farmwareNames: [],
|
||||
firstPartyFarmwareNames: [],
|
||||
showFirstPartyFarmware: false,
|
||||
farmwareConfigs: {},
|
||||
},
|
||||
farmwareData: fakeFarmwareData(),
|
||||
shouldDisplay: jest.fn(),
|
||||
getWebAppConfigValue: jest.fn(),
|
||||
menuOpen: false,
|
||||
|
|
|
@ -4,31 +4,25 @@ const mockData = {
|
|||
fakeSequences: [fakeSequence()]
|
||||
};
|
||||
|
||||
jest.mock("../../util/urls", () => {
|
||||
return {
|
||||
urlFriendly: jest.fn(x => x),
|
||||
lastUrlChunk: jest.fn(() => mockData.lastUrlChunk)
|
||||
};
|
||||
});
|
||||
jest.mock("../../util/urls", () => ({
|
||||
urlFriendly: jest.fn(x => x),
|
||||
lastUrlChunk: jest.fn(() => mockData.lastUrlChunk)
|
||||
}));
|
||||
|
||||
jest.mock("../actions", () => ({ selectSequence: jest.fn() }));
|
||||
|
||||
jest.mock("../../resources/selectors", () => {
|
||||
return {
|
||||
selectAllSequences: jest.fn(() => {
|
||||
return mockData.fakeSequences || [];
|
||||
})
|
||||
};
|
||||
});
|
||||
jest.mock("../../resources/selectors", () => ({
|
||||
selectAllSequences: jest.fn(() => mockData.fakeSequences || []),
|
||||
}));
|
||||
|
||||
jest.mock("../../redux/store", () => {
|
||||
return {
|
||||
store: {
|
||||
dispatch: jest.fn(),
|
||||
getState: jest.fn(() => ({ resources: { index: {} } }))
|
||||
}
|
||||
};
|
||||
});
|
||||
jest.mock("../../redux/store", () => ({
|
||||
store: {
|
||||
dispatch: jest.fn(),
|
||||
getState: jest.fn(() => ({ resources: { index: {} } }))
|
||||
}
|
||||
}));
|
||||
|
||||
jest.mock("../../account/dev/dev_support", () => ({}));
|
||||
|
||||
import { setActiveSequenceByName } from "../set_active_sequence_by_name";
|
||||
import { selectSequence } from "../actions";
|
||||
|
|
|
@ -3,7 +3,7 @@ import { fakeState } from "../../__test_support__/fake_state";
|
|||
import { Feature } from "../../devices/interfaces";
|
||||
import { fakeFarmwareManifestV1 } from "../../__test_support__/fake_farmwares";
|
||||
import {
|
||||
fakeSequence, fakeWebAppConfig
|
||||
fakeSequence, fakeWebAppConfig, fakeFarmwareEnv
|
||||
} from "../../__test_support__/fake_state/resources";
|
||||
import {
|
||||
buildResourceIndex
|
||||
|
@ -67,12 +67,24 @@ describe("mapStateToProps()", () => {
|
|||
"My Fake Farmware": fakeFarmwareManifestV1()
|
||||
};
|
||||
const props = mapStateToProps(state);
|
||||
expect(props.farmwareInfo.farmwareNames).toEqual(["My Fake Farmware"]);
|
||||
expect(props.farmwareInfo.showFirstPartyFarmware).toEqual(true);
|
||||
expect(props.farmwareInfo.farmwareConfigs).toEqual({
|
||||
expect(props.farmwareData.farmwareNames).toEqual(["My Fake Farmware"]);
|
||||
expect(props.farmwareData.showFirstPartyFarmware).toEqual(true);
|
||||
expect(props.farmwareData.farmwareConfigs).toEqual({
|
||||
"My Fake Farmware": [{
|
||||
name: "config_1", label: "Config 1", value: "4"
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
it("returns api props", () => {
|
||||
const state = fakeState();
|
||||
const fakeEnv = fakeFarmwareEnv();
|
||||
fakeEnv.body.key = "camera";
|
||||
fakeEnv.body.value = "NONE";
|
||||
state.resources = buildResourceIndex([fakeEnv]);
|
||||
state.bot.minOsFeatureData = { api_farmware_env: "8.0.0" };
|
||||
state.bot.hardware.informational_settings.controller_version = "8.0.0";
|
||||
const props = mapStateToProps(state);
|
||||
expect(props.farmwareData.cameraDisabled).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@ import { StepDragger } from "../draggable/step_dragger";
|
|||
import { renderCeleryNode } from "./step_tiles/index";
|
||||
import { ResourceIndex } from "../resources/interfaces";
|
||||
import { getStepTag } from "../resources/sequence_tagging";
|
||||
import { HardwareFlags, FarmwareInfo } from "./interfaces";
|
||||
import { HardwareFlags, FarmwareData } from "./interfaces";
|
||||
import { ShouldDisplay } from "../devices/interfaces";
|
||||
import { AddCommandButton } from "./sequence_editor_middle_active";
|
||||
import { ErrorBoundary } from "../error_boundary";
|
||||
|
@ -18,7 +18,7 @@ export interface AllStepsProps {
|
|||
dispatch: Function;
|
||||
resources: ResourceIndex;
|
||||
hardwareFlags?: HardwareFlags;
|
||||
farmwareInfo?: FarmwareInfo;
|
||||
farmwareData?: FarmwareData;
|
||||
shouldDisplay?: ShouldDisplay;
|
||||
confirmStepDeletion: boolean;
|
||||
showPins?: boolean;
|
||||
|
@ -43,7 +43,7 @@ export class AllSteps extends React.Component<AllStepsProps, {}> {
|
|||
currentSequence: sequence,
|
||||
resources: this.props.resources,
|
||||
hardwareFlags: this.props.hardwareFlags,
|
||||
farmwareInfo: this.props.farmwareInfo,
|
||||
farmwareData: this.props.farmwareData,
|
||||
shouldDisplay: this.props.shouldDisplay,
|
||||
confirmStepDeletion: this.props.confirmStepDeletion,
|
||||
showPins: this.props.showPins,
|
||||
|
|
|
@ -44,7 +44,7 @@ export interface Props {
|
|||
resources: ResourceIndex;
|
||||
syncStatus: SyncStatus;
|
||||
hardwareFlags: HardwareFlags;
|
||||
farmwareInfo: FarmwareInfo;
|
||||
farmwareData: FarmwareData;
|
||||
shouldDisplay: ShouldDisplay;
|
||||
getWebAppConfigValue: GetWebAppConfigValue;
|
||||
menuOpen: boolean;
|
||||
|
@ -58,7 +58,7 @@ export interface SequenceEditorMiddleProps {
|
|||
resources: ResourceIndex;
|
||||
syncStatus: SyncStatus;
|
||||
hardwareFlags: HardwareFlags;
|
||||
farmwareInfo: FarmwareInfo;
|
||||
farmwareData: FarmwareData;
|
||||
shouldDisplay: ShouldDisplay;
|
||||
getWebAppConfigValue: GetWebAppConfigValue;
|
||||
menuOpen: boolean;
|
||||
|
@ -189,11 +189,12 @@ export type dispatcher = (a: Function | { type: string }) => DataXferObj;
|
|||
|
||||
export type FarmwareConfigs = { [x: string]: FarmwareConfig[] };
|
||||
|
||||
export interface FarmwareInfo {
|
||||
export interface FarmwareData {
|
||||
farmwareNames: string[];
|
||||
firstPartyFarmwareNames: string[];
|
||||
showFirstPartyFarmware: boolean;
|
||||
farmwareConfigs: FarmwareConfigs;
|
||||
cameraDisabled: boolean;
|
||||
}
|
||||
|
||||
export interface StepParams {
|
||||
|
@ -203,7 +204,7 @@ export interface StepParams {
|
|||
index: number;
|
||||
resources: ResourceIndex;
|
||||
hardwareFlags?: HardwareFlags;
|
||||
farmwareInfo?: FarmwareInfo;
|
||||
farmwareData?: FarmwareData;
|
||||
shouldDisplay?: ShouldDisplay;
|
||||
confirmStepDeletion: boolean;
|
||||
showPins?: boolean;
|
||||
|
|
|
@ -23,7 +23,7 @@ export class SequenceEditorMiddle
|
|||
resources={this.props.resources}
|
||||
syncStatus={this.props.syncStatus}
|
||||
hardwareFlags={this.props.hardwareFlags}
|
||||
farmwareInfo={this.props.farmwareInfo}
|
||||
farmwareData={this.props.farmwareData}
|
||||
shouldDisplay={this.props.shouldDisplay}
|
||||
getWebAppConfigValue={this.props.getWebAppConfigValue}
|
||||
menuOpen={this.props.menuOpen} />}
|
||||
|
|
|
@ -249,7 +249,7 @@ export class SequenceEditorMiddleActive extends
|
|||
dispatch: this.props.dispatch,
|
||||
resources: this.props.resources,
|
||||
hardwareFlags: this.props.hardwareFlags,
|
||||
farmwareInfo: this.props.farmwareInfo,
|
||||
farmwareData: this.props.farmwareData,
|
||||
shouldDisplay: this.props.shouldDisplay,
|
||||
confirmStepDeletion: !!getConfig(BooleanSetting.confirm_step_deletion),
|
||||
showPins: !!getConfig(BooleanSetting.show_pins),
|
||||
|
|
|
@ -58,7 +58,7 @@ export class RawSequences extends React.Component<Props, {}> {
|
|||
sequence={this.props.sequence}
|
||||
resources={this.props.resources}
|
||||
hardwareFlags={this.props.hardwareFlags}
|
||||
farmwareInfo={this.props.farmwareInfo}
|
||||
farmwareData={this.props.farmwareData}
|
||||
shouldDisplay={this.props.shouldDisplay}
|
||||
getWebAppConfigValue={this.props.getWebAppConfigValue}
|
||||
menuOpen={this.props.menuOpen} />
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
import { Everything } from "../interfaces";
|
||||
import { Props, HardwareFlags, FarmwareConfigs } from "./interfaces";
|
||||
import {
|
||||
selectAllSequences, findSequence, maybeGetDevice
|
||||
} from "../resources/selectors";
|
||||
import { selectAllSequences, findSequence } from "../resources/selectors";
|
||||
import { getStepTag } from "../resources/sequence_tagging";
|
||||
import { enabledAxisMap } from "../devices/components/axis_tracking_status";
|
||||
import {
|
||||
createShouldDisplayFn as shouldDisplayFunc,
|
||||
determineInstalledOsVersion, validFwConfig
|
||||
} from "../util";
|
||||
import { validFwConfig } from "../util";
|
||||
import { BooleanSetting } from "../session_keys";
|
||||
import { getWebAppConfigValue } from "../config_storage/actions";
|
||||
import { getFirmwareConfig } from "../resources/getters";
|
||||
import { Farmwares } from "../farmware/interfaces";
|
||||
import { manifestInfo } from "../farmware/generate_manifest_info";
|
||||
import { DevSettings } from "../account/dev/dev_support";
|
||||
import { calculateAxialLengths } from "../controls/move/direction_axes_props";
|
||||
import { mapStateToFolderProps } from "../folders/map_state_to_props";
|
||||
import { getEnv, getShouldDisplayFn } from "../farmware/state_to_props";
|
||||
import {
|
||||
cameraDisabled
|
||||
} from "../devices/components/fbos_settings/camera_selection";
|
||||
|
||||
export function mapStateToProps(props: Everything): Props {
|
||||
const uuid = props.resources.consumers.sequences.current;
|
||||
|
@ -62,11 +60,8 @@ export function mapStateToProps(props: Everything): Props {
|
|||
const farmwareConfigs: FarmwareConfigs = {};
|
||||
Object.values(farmwares).map(fw => farmwareConfigs[fw.name] = fw.config);
|
||||
|
||||
const installedOsVersion = determineInstalledOsVersion(
|
||||
props.bot, maybeGetDevice(props.resources.index));
|
||||
const fbosVersionOverride = DevSettings.overriddenFbosVersion();
|
||||
const shouldDisplay = shouldDisplayFunc(
|
||||
installedOsVersion, props.bot.minOsFeatureData, fbosVersionOverride);
|
||||
const shouldDisplay = getShouldDisplayFn(props.resources.index, props.bot);
|
||||
const env = getEnv(props.resources.index, shouldDisplay, props.bot);
|
||||
|
||||
return {
|
||||
dispatch: props.dispatch,
|
||||
|
@ -79,11 +74,12 @@ export function mapStateToProps(props: Everything): Props {
|
|||
.informational_settings
|
||||
.sync_status || "unknown"),
|
||||
hardwareFlags: hardwareFlags(),
|
||||
farmwareInfo: {
|
||||
farmwareData: {
|
||||
farmwareNames,
|
||||
firstPartyFarmwareNames,
|
||||
showFirstPartyFarmware,
|
||||
farmwareConfigs,
|
||||
cameraDisabled: cameraDisabled(env),
|
||||
},
|
||||
shouldDisplay,
|
||||
getWebAppConfigValue: getConfig,
|
||||
|
|
|
@ -6,7 +6,7 @@ import { emptyState } from "../../../resources/reducer";
|
|||
import { HardwareFlags } from "../../interfaces";
|
||||
import {
|
||||
fakeHardwareFlags
|
||||
} from "../../../__test_support__/sequence_hardware_settings";
|
||||
} from "../../../__test_support__/fake_sequence_step_data";
|
||||
|
||||
describe("<TileCalibrate/>", () => {
|
||||
const fakeProps = (): CalibrateParams => ({
|
||||
|
|
|
@ -4,8 +4,11 @@ import { mount, shallow } from "enzyme";
|
|||
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
|
||||
import { ExecuteScript } from "farmbot/dist";
|
||||
import { StepParams } from "../../interfaces";
|
||||
import { Actions } from "../../../constants";
|
||||
import { Actions, Content } from "../../../constants";
|
||||
import { emptyState } from "../../../resources/reducer";
|
||||
import {
|
||||
fakeFarmwareData
|
||||
} from "../../../__test_support__/fake_sequence_step_data";
|
||||
|
||||
describe("<TileExecuteScript/>", () => {
|
||||
const fakeProps = (): StepParams => {
|
||||
|
@ -15,18 +18,17 @@ describe("<TileExecuteScript/>", () => {
|
|||
label: "farmware-to-execute"
|
||||
}
|
||||
};
|
||||
const farmwareData = fakeFarmwareData();
|
||||
farmwareData.farmwareNames = ["one", "two", "three"];
|
||||
farmwareData.firstPartyFarmwareNames = ["one"];
|
||||
farmwareData.farmwareConfigs = { "farmware-to-execute": [] };
|
||||
return {
|
||||
currentSequence: fakeSequence(),
|
||||
currentStep,
|
||||
dispatch: jest.fn(),
|
||||
index: 0,
|
||||
resources: emptyState().index,
|
||||
farmwareInfo: {
|
||||
farmwareNames: ["one", "two", "three"],
|
||||
firstPartyFarmwareNames: ["one"],
|
||||
showFirstPartyFarmware: false,
|
||||
farmwareConfigs: { "farmware-to-execute": [] },
|
||||
},
|
||||
farmwareData,
|
||||
confirmStepDeletion: false,
|
||||
};
|
||||
};
|
||||
|
@ -61,7 +63,7 @@ describe("<TileExecuteScript/>", () => {
|
|||
|
||||
it("shows 1st party in list", () => {
|
||||
const p = fakeProps();
|
||||
p.farmwareInfo && (p.farmwareInfo.showFirstPartyFarmware = true);
|
||||
p.farmwareData && (p.farmwareData.showFirstPartyFarmware = true);
|
||||
const wrapper = shallow(<TileExecuteScript {...p} />);
|
||||
expect(wrapper.find("FBSelect").props().list).toEqual([
|
||||
{ label: "one", value: "one" },
|
||||
|
@ -81,7 +83,7 @@ describe("<TileExecuteScript/>", () => {
|
|||
it("shows special 1st-party Farmware name", () => {
|
||||
const p = fakeProps();
|
||||
(p.currentStep as ExecuteScript).args.label = "plant-detection";
|
||||
p.farmwareInfo && p.farmwareInfo.farmwareNames.push("plant-detection");
|
||||
p.farmwareData && p.farmwareData.farmwareNames.push("plant-detection");
|
||||
const wrapper = mount(<TileExecuteScript {...p} />);
|
||||
expect(wrapper.find("label").length).toEqual(1);
|
||||
expect(wrapper.text()).toContain("Weed Detector");
|
||||
|
@ -89,7 +91,7 @@ describe("<TileExecuteScript/>", () => {
|
|||
|
||||
it("renders manual input", () => {
|
||||
const p = fakeProps();
|
||||
p.farmwareInfo = undefined;
|
||||
p.farmwareData = undefined;
|
||||
const wrapper = mount(<TileExecuteScript {...p} />);
|
||||
expect(wrapper.find("button").text()).toEqual("Manual Input");
|
||||
expect(wrapper.find("label").at(1).text()).toEqual("Manual input");
|
||||
|
@ -131,4 +133,12 @@ describe("<TileExecuteScript/>", () => {
|
|||
type: Actions.OVERWRITE_RESOURCE
|
||||
});
|
||||
});
|
||||
|
||||
it("displays warning when camera is disabled", () => {
|
||||
const p = fakeProps();
|
||||
(p.currentStep as ExecuteScript).args.label = "plant-detection";
|
||||
p.farmwareData && (p.farmwareData.cameraDisabled = true);
|
||||
const wrapper = mount(<TileExecuteScript {...p} />);
|
||||
expect(wrapper.text()).toContain(Content.NO_CAMERA_SELECTED);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@ import { mount } from "enzyme";
|
|||
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
|
||||
import {
|
||||
fakeHardwareFlags
|
||||
} from "../../../__test_support__/sequence_hardware_settings";
|
||||
} from "../../../__test_support__/fake_sequence_step_data";
|
||||
import { HardwareFlags } from "../../interfaces";
|
||||
import { emptyState } from "../../../resources/reducer";
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import { mount } from "enzyme";
|
|||
import { MoveAbsoluteWarningProps } from "../../interfaces";
|
||||
import {
|
||||
fakeHardwareFlags
|
||||
} from "../../../__test_support__/sequence_hardware_settings";
|
||||
} from "../../../__test_support__/fake_sequence_step_data";
|
||||
import { MoveAbsoluteWarning } from "../tile_move_absolute_conflict_check";
|
||||
|
||||
describe("<MoveAbsoluteWarning/>", () => {
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
} from "farmbot";
|
||||
import {
|
||||
fakeHardwareFlags
|
||||
} from "../../../__test_support__/sequence_hardware_settings";
|
||||
} from "../../../__test_support__/fake_sequence_step_data";
|
||||
import { emptyState } from "../../../resources/reducer";
|
||||
import { inputEvent } from "../../../__test_support__/fake_input_event";
|
||||
import { StepParams } from "../../interfaces";
|
||||
|
|
|
@ -5,6 +5,10 @@ import { fakeSequence } from "../../../__test_support__/fake_state/resources";
|
|||
import { TakePhoto } from "farmbot/dist";
|
||||
import { StepParams } from "../../interfaces";
|
||||
import { emptyState } from "../../../resources/reducer";
|
||||
import {
|
||||
fakeFarmwareData
|
||||
} from "../../../__test_support__/fake_sequence_step_data";
|
||||
import { Content } from "../../../constants";
|
||||
|
||||
describe("<TileTakePhoto/>", () => {
|
||||
const currentStep: TakePhoto = {
|
||||
|
@ -19,6 +23,7 @@ describe("<TileTakePhoto/>", () => {
|
|||
index: 0,
|
||||
resources: emptyState().index,
|
||||
confirmStepDeletion: false,
|
||||
farmwareData: fakeFarmwareData(),
|
||||
});
|
||||
|
||||
it("renders step", () => {
|
||||
|
@ -34,4 +39,11 @@ describe("<TileTakePhoto/>", () => {
|
|||
expect(inputs.first().props().placeholder).toEqual("Take a Photo");
|
||||
expect(wrapper.text()).toContain("farmware page");
|
||||
});
|
||||
|
||||
it("displays warning when camera is disabled", () => {
|
||||
const p = fakeProps();
|
||||
p.farmwareData && (p.farmwareData.cameraDisabled = true);
|
||||
const wrapper = mount(<TileTakePhoto {...p} />);
|
||||
expect(wrapper.text()).toContain(Content.NO_CAMERA_SELECTED);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import * as React from "react";
|
||||
import { StepParams } from "../interfaces";
|
||||
import { ToolTips } from "../../constants";
|
||||
import { ToolTips, Content } from "../../constants";
|
||||
import { StepInputBox } from "../inputs/step_input_box";
|
||||
import { StepWrapper, StepHeader, StepContent } from "../step_ui/index";
|
||||
import {
|
||||
StepWrapper, StepHeader, StepContent, StepWarning
|
||||
} from "../step_ui/index";
|
||||
import { Row, Col, FBSelect, DropDownItem } from "../../ui/index";
|
||||
import { editStep } from "../../api/crud";
|
||||
import { ExecuteScript, FarmwareConfig } from "farmbot";
|
||||
|
@ -10,14 +12,14 @@ import { FarmwareInputs, farmwareList } from "./tile_execute_script_support";
|
|||
import { t } from "../../i18next_wrapper";
|
||||
|
||||
export function TileExecuteScript(props: StepParams) {
|
||||
const { dispatch, currentStep, index, currentSequence, farmwareInfo } = props;
|
||||
const { dispatch, currentStep, index, currentSequence, farmwareData } = props;
|
||||
if (currentStep.kind === "execute_script") {
|
||||
|
||||
const farmwareName = currentStep.args.label;
|
||||
|
||||
/** Selected Farmware is installed on connected bot. */
|
||||
const isInstalled = (name: string): boolean => {
|
||||
return !!(farmwareInfo && farmwareInfo.farmwareNames.includes(name));
|
||||
return !!(farmwareData && farmwareData.farmwareNames.includes(name));
|
||||
};
|
||||
|
||||
const selectedFarmwareDDI = (name: string): DropDownItem => {
|
||||
|
@ -52,8 +54,8 @@ export function TileExecuteScript(props: StepParams) {
|
|||
|
||||
/** Configs (inputs) from Farmware manifest for <FarmwareInputs />. */
|
||||
const currentFarmwareConfigDefaults = (fwName: string): FarmwareConfig[] => {
|
||||
return farmwareInfo && farmwareInfo.farmwareConfigs[fwName]
|
||||
? farmwareInfo.farmwareConfigs[fwName]
|
||||
return farmwareData && farmwareData.farmwareConfigs[fwName]
|
||||
? farmwareData.farmwareConfigs[fwName]
|
||||
: [];
|
||||
};
|
||||
|
||||
|
@ -66,14 +68,20 @@ export function TileExecuteScript(props: StepParams) {
|
|||
currentStep={currentStep}
|
||||
dispatch={dispatch}
|
||||
index={index}
|
||||
confirmStepDeletion={props.confirmStepDeletion} />
|
||||
confirmStepDeletion={props.confirmStepDeletion}>
|
||||
{props.farmwareData && props.farmwareData.cameraDisabled &&
|
||||
(farmwareName === "plant-detection") &&
|
||||
<StepWarning
|
||||
titleBase={t(Content.NO_CAMERA_SELECTED)}
|
||||
warning={t(ToolTips.SELECT_A_CAMERA)} />}
|
||||
</StepHeader>
|
||||
<StepContent className={className}>
|
||||
<Row>
|
||||
<Col xs={12}>
|
||||
<label>{t("Package Name")}</label>
|
||||
<FBSelect
|
||||
key={JSON.stringify(props.currentSequence)}
|
||||
list={farmwareList(farmwareInfo)}
|
||||
list={farmwareList(farmwareData)}
|
||||
selectedItem={selectedFarmwareDDI(farmwareName)}
|
||||
onChange={updateStepFarmwareSelection}
|
||||
allowEmpty={true}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from "react";
|
||||
import { FarmwareInfo } from "../interfaces";
|
||||
import { FarmwareData } from "../interfaces";
|
||||
import { DropDownItem, BlurableInput, Help } from "../../ui/index";
|
||||
import { without, isNumber } from "lodash";
|
||||
import { ExecuteScript, Pair, FarmwareConfig } from "farmbot";
|
||||
|
@ -113,11 +113,11 @@ const farmwareInputs =
|
|||
|
||||
/** List of installed Farmware, if bot is connected (for DropDown). */
|
||||
export const farmwareList =
|
||||
(farmwareInfo: FarmwareInfo | undefined): DropDownItem[] => {
|
||||
if (farmwareInfo) {
|
||||
(farmwareData: FarmwareData | undefined): DropDownItem[] => {
|
||||
if (farmwareData) {
|
||||
const {
|
||||
farmwareNames, showFirstPartyFarmware, firstPartyFarmwareNames
|
||||
} = farmwareInfo;
|
||||
} = farmwareData;
|
||||
return farmwareNames
|
||||
.filter(x => (firstPartyFarmwareNames && !showFirstPartyFarmware)
|
||||
? !firstPartyFarmwareNames.includes(x) : x)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from "react";
|
||||
import { StepParams } from "../interfaces";
|
||||
import { ToolTips } from "../../constants";
|
||||
import { StepWrapper, StepHeader, StepContent } from "../step_ui";
|
||||
import { ToolTips, Content } from "../../constants";
|
||||
import { StepWrapper, StepHeader, StepContent, StepWarning } from "../step_ui";
|
||||
import { Col, Row } from "../../ui/index";
|
||||
import { Link } from "../../link";
|
||||
import { t } from "../../i18next_wrapper";
|
||||
|
@ -17,7 +17,12 @@ export function TileTakePhoto(props: StepParams) {
|
|||
currentStep={currentStep}
|
||||
dispatch={dispatch}
|
||||
index={index}
|
||||
confirmStepDeletion={props.confirmStepDeletion} />
|
||||
confirmStepDeletion={props.confirmStepDeletion}>
|
||||
{props.farmwareData && props.farmwareData.cameraDisabled &&
|
||||
<StepWarning
|
||||
titleBase={t(Content.NO_CAMERA_SELECTED)}
|
||||
warning={t(ToolTips.SELECT_A_CAMERA)} />}
|
||||
</StepHeader>
|
||||
<StepContent className={className}>
|
||||
<Row>
|
||||
<Col xs={12}>
|
||||
|
|
|
@ -6,6 +6,7 @@ import { t } from "../../i18next_wrapper";
|
|||
interface StepWarningProps {
|
||||
warning: string;
|
||||
conflicts?: Record<Xyz, boolean>;
|
||||
titleBase?: string;
|
||||
}
|
||||
|
||||
const TITLE_BASE = t("Hardware setting conflict");
|
||||
|
@ -20,11 +21,10 @@ export const conflictsString = (conflicts: Record<Xyz, boolean>) => {
|
|||
};
|
||||
|
||||
export function StepWarning(props: StepWarningProps) {
|
||||
const { conflicts, warning } = props;
|
||||
const { conflicts, warning, titleBase } = props;
|
||||
const warningTitle = () => {
|
||||
return conflicts
|
||||
? TITLE_BASE + ": " + conflictsString(conflicts)
|
||||
: TITLE_BASE;
|
||||
return (titleBase || TITLE_BASE) +
|
||||
(conflicts ? ": " + conflictsString(conflicts) : "");
|
||||
};
|
||||
return <div className="step-warning">
|
||||
<Popover
|
||||
|
|
Loading…
Reference in New Issue