Merge branch 'master' into master
commit
f22b209872
|
@ -28,7 +28,7 @@ You will need the following:
|
||||||
|
|
||||||
1. A Linux or Mac based machine. We do not support windows at this time.
|
1. A Linux or Mac based machine. We do not support windows at this time.
|
||||||
0. [Docker 17.06.0-ce or greater](https://docs.docker.com/engine/installation/)
|
0. [Docker 17.06.0-ce or greater](https://docs.docker.com/engine/installation/)
|
||||||
0. [Ruby 2.4.1](http://rvm.io/rvm/install)
|
0. [Ruby 2.4.2](http://rvm.io/rvm/install)
|
||||||
0. [ImageMagick](https://www.imagemagick.org/script/index.php) (`brew install imagemagick` (Mac) or `sudo apt-get install imagemagick` (Ubuntu))
|
0. [ImageMagick](https://www.imagemagick.org/script/index.php) (`brew install imagemagick` (Mac) or `sudo apt-get install imagemagick` (Ubuntu))
|
||||||
0. [Node JS > v6](https://nodejs.org/en/download/)
|
0. [Node JS > v6](https://nodejs.org/en/download/)
|
||||||
0. [`libpq-dev` and `postgresql`](http://stackoverflow.com/questions/6040583/cant-find-the-libpq-fe-h-header-when-trying-to-install-pg-gem/6040822#6040822) and `postgresql-contrib`
|
0. [`libpq-dev` and `postgresql`](http://stackoverflow.com/questions/6040583/cant-find-the-libpq-fe-h-header-when-trying-to-install-pg-gem/6040822#6040822) and `postgresql-contrib`
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { BooleanSetting } from "../session_keys";
|
||||||
|
|
||||||
describe("<LoadingPlant/>", () => {
|
describe("<LoadingPlant/>", () => {
|
||||||
it("renders loading text", () => {
|
it("renders loading text", () => {
|
||||||
|
mockStorj[BooleanSetting.disableAnimations] = true;
|
||||||
const wrapper = shallow(<LoadingPlant />);
|
const wrapper = shallow(<LoadingPlant />);
|
||||||
expect(wrapper.find(".loading-plant").length).toEqual(0);
|
expect(wrapper.find(".loading-plant").length).toEqual(0);
|
||||||
expect(wrapper.find(".loading-plant-text").props().y).toEqual(150);
|
expect(wrapper.find(".loading-plant-text").props().y).toEqual(150);
|
||||||
|
@ -27,7 +28,7 @@ describe("<LoadingPlant/>", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders loading animation", () => {
|
it("renders loading animation", () => {
|
||||||
mockStorj[BooleanSetting.plantAnimations] = true;
|
mockStorj[BooleanSetting.disableAnimations] = false;
|
||||||
const wrapper = shallow(<LoadingPlant />);
|
const wrapper = shallow(<LoadingPlant />);
|
||||||
expect(wrapper.find(".loading-plant")).toBeTruthy();
|
expect(wrapper.find(".loading-plant")).toBeTruthy();
|
||||||
const circleProps = wrapper.find(".loading-plant-circle").props();
|
const circleProps = wrapper.find(".loading-plant-circle").props();
|
||||||
|
|
|
@ -11,14 +11,16 @@ export interface LabsFeature {
|
||||||
storageKey: BooleanSetting;
|
storageKey: BooleanSetting;
|
||||||
value: boolean;
|
value: boolean;
|
||||||
experimental?: boolean;
|
experimental?: boolean;
|
||||||
|
displayInvert?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchLabFeatures = (): LabsFeature[] => ([
|
export const fetchLabFeatures = (): LabsFeature[] => ([
|
||||||
{
|
{
|
||||||
name: t("Disable Web App internationalization"),
|
name: t("Internationalize Web App"),
|
||||||
description: t("Set Web App to English."),
|
description: t("Turn off to set Web App to English."),
|
||||||
storageKey: BooleanSetting.disableI18n,
|
storageKey: BooleanSetting.disableI18n,
|
||||||
value: false
|
value: false,
|
||||||
|
displayInvert: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: t("Confirm Sequence step deletion"),
|
name: t("Confirm Sequence step deletion"),
|
||||||
|
@ -51,9 +53,10 @@ export const fetchLabFeatures = (): LabsFeature[] => ([
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: t("Display plant animations"),
|
name: t("Display plant animations"),
|
||||||
description: trim(t(`Turn on plant animations in the Farm Designer.`)),
|
description: trim(t(`Enable plant animations in the Farm Designer.`)),
|
||||||
storageKey: BooleanSetting.plantAnimations,
|
storageKey: BooleanSetting.disableAnimations,
|
||||||
value: true
|
value: false,
|
||||||
|
displayInvert: true
|
||||||
}
|
}
|
||||||
].map(fetchRealValue));
|
].map(fetchRealValue));
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,12 @@ interface LabsFeaturesListProps {
|
||||||
export function LabsFeaturesList(props: LabsFeaturesListProps) {
|
export function LabsFeaturesList(props: LabsFeaturesListProps) {
|
||||||
return <div>
|
return <div>
|
||||||
{fetchLabFeatures().map((p, i) => {
|
{fetchLabFeatures().map((p, i) => {
|
||||||
|
const displayValue = p.displayInvert ? !p.value : p.value;
|
||||||
return <KeyValShowRow key={i}
|
return <KeyValShowRow key={i}
|
||||||
label={p.name}
|
label={p.name}
|
||||||
labelPlaceholder=""
|
labelPlaceholder=""
|
||||||
value={p.description}
|
value={p.description}
|
||||||
toggleValue={p.value ? 1 : 0}
|
toggleValue={displayValue ? 1 : 0}
|
||||||
valuePlaceholder=""
|
valuePlaceholder=""
|
||||||
onClick={() => props.onToggle(p)}
|
onClick={() => props.onToggle(p)}
|
||||||
disabled={false} />;
|
disabled={false} />;
|
||||||
|
|
|
@ -10,6 +10,10 @@
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@media screen and (max-width: 974px) {
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regimen Editor
|
// Regimen Editor
|
||||||
|
|
|
@ -13,20 +13,34 @@
|
||||||
margin-top: 0.4rem;
|
margin-top: 0.4rem;
|
||||||
}
|
}
|
||||||
@media screen and (max-width: 974px) {
|
@media screen and (max-width: 974px) {
|
||||||
|
h3, p {
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
h3 {
|
h3 {
|
||||||
margin-bottom: 2.5rem;
|
margin-bottom: 2.5rem;
|
||||||
}
|
}
|
||||||
|
.button-group {
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sequence-editor-content,
|
.sequence-editor-content,
|
||||||
.regimen-editor-content {
|
.regimen-editor-content {
|
||||||
margin-right: -15px;
|
margin-right: -15px;
|
||||||
|
@media screen and (max-width: 974px) {
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sequence-editor-tools,
|
.sequence-editor-tools,
|
||||||
.regimen-editor-tools {
|
.regimen-editor-tools {
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
|
@media screen and (max-width: 974px) {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sequence,
|
.sequence,
|
||||||
|
@ -47,7 +61,7 @@
|
||||||
.regimen-list {
|
.regimen-list {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
height: calc(100vh - 21rem);
|
max-height: calc(100vh - 21rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.step-button-cluster,
|
.step-button-cluster,
|
||||||
|
@ -55,6 +69,20 @@
|
||||||
margin-right: -15px;
|
margin-right: -15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.step-button-cluster-panel {
|
||||||
|
@media screen and (max-width: 974px) {
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-button-cluster {
|
||||||
|
@media screen and (max-width: 974px) {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.sequence-list-items {
|
.sequence-list-items {
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
}
|
}
|
||||||
|
@ -64,6 +92,10 @@
|
||||||
padding-top: 0.4rem;
|
padding-top: 0.4rem;
|
||||||
margin-bottom: 3rem;
|
margin-bottom: 3rem;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
|
@media screen and (max-width: 974px) {
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sequence-list-panel input,
|
.sequence-list-panel input,
|
||||||
|
|
|
@ -136,6 +136,7 @@ export interface PeripheralsProps {
|
||||||
export interface FarmwareProps {
|
export interface FarmwareProps {
|
||||||
dispatch: Function;
|
dispatch: Function;
|
||||||
env: Partial<WD_ENV>;
|
env: Partial<WD_ENV>;
|
||||||
|
user_env: Record<string, string | undefined>;
|
||||||
images: TaggedImage[];
|
images: TaggedImage[];
|
||||||
currentImage: TaggedImage | undefined;
|
currentImage: TaggedImage | undefined;
|
||||||
syncStatus: SyncStatus;
|
syncStatus: SyncStatus;
|
||||||
|
|
|
@ -36,6 +36,7 @@ describe("<GardenPlant/>", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
it("renders plant", () => {
|
it("renders plant", () => {
|
||||||
|
mockStorj[BooleanSetting.disableAnimations] = true;
|
||||||
const wrapper = shallow(<GardenPlant {...fakeProps() } />);
|
const wrapper = shallow(<GardenPlant {...fakeProps() } />);
|
||||||
expect(wrapper.find("image").length).toEqual(1);
|
expect(wrapper.find("image").length).toEqual(1);
|
||||||
expect(wrapper.find("image").props().opacity).toEqual(1);
|
expect(wrapper.find("image").props().opacity).toEqual(1);
|
||||||
|
@ -46,7 +47,7 @@ describe("<GardenPlant/>", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders plant animations", () => {
|
it("renders plant animations", () => {
|
||||||
mockStorj[BooleanSetting.plantAnimations] = true;
|
mockStorj[BooleanSetting.disableAnimations] = false;
|
||||||
const wrapper = shallow(<GardenPlant {...fakeProps() } />);
|
const wrapper = shallow(<GardenPlant {...fakeProps() } />);
|
||||||
expect(wrapper.find(".soil-cloud").length).toEqual(1);
|
expect(wrapper.find(".soil-cloud").length).toEqual(1);
|
||||||
expect(wrapper.find(".animate").length).toEqual(1);
|
expect(wrapper.find(".animate").length).toEqual(1);
|
||||||
|
|
|
@ -37,7 +37,7 @@ export class GardenPlant extends
|
||||||
|
|
||||||
const { qx, qy } = getXYFromQuadrant(round(x), round(y), quadrant, gridSize);
|
const { qx, qy } = getXYFromQuadrant(round(x), round(y), quadrant, gridSize);
|
||||||
const alpha = dragging ? 0.4 : 1.0;
|
const alpha = dragging ? 0.4 : 1.0;
|
||||||
const animate = Session.getBool(BooleanSetting.plantAnimations);
|
const animate = !Session.getBool(BooleanSetting.disableAnimations);
|
||||||
|
|
||||||
return <g id={"plant-" + id}>
|
return <g id={"plant-" + id}>
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
jest.mock("../../../../session", () => {
|
jest.mock("../../../../session", () => {
|
||||||
return {
|
return {
|
||||||
Session: {
|
Session: {
|
||||||
getBool: () => { return true; }
|
getBool: () => { return false; }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -47,7 +47,7 @@ export class HoveredPlantLayer extends
|
||||||
const hovered = !!this.props.designer.hoveredPlant.icon;
|
const hovered = !!this.props.designer.hoveredPlant.icon;
|
||||||
const scaledRadius = currentPlant ? radius : radius * 1.2;
|
const scaledRadius = currentPlant ? radius : radius * 1.2;
|
||||||
const alpha = dragging ? 0.4 : 1.0;
|
const alpha = dragging ? 0.4 : 1.0;
|
||||||
const animate = Session.getBool(BooleanSetting.plantAnimations);
|
const animate = !Session.getBool(BooleanSetting.disableAnimations);
|
||||||
|
|
||||||
return <g id="hovered-plant-layer">
|
return <g id="hovered-plant-layer">
|
||||||
{this.props.visible && hovered &&
|
{this.props.visible && hovered &&
|
||||||
|
|
|
@ -83,7 +83,7 @@ export class SpreadCircle extends
|
||||||
const { selected, mapTransformProps } = this.props;
|
const { selected, mapTransformProps } = this.props;
|
||||||
const { quadrant, gridSize } = mapTransformProps;
|
const { quadrant, gridSize } = mapTransformProps;
|
||||||
const { qx, qy } = getXYFromQuadrant(round(x), round(y), quadrant, gridSize);
|
const { qx, qy } = getXYFromQuadrant(round(x), round(y), quadrant, gridSize);
|
||||||
const animate = Session.getBool(BooleanSetting.plantAnimations);
|
const animate = !Session.getBool(BooleanSetting.disableAnimations);
|
||||||
|
|
||||||
return <g id={"spread-" + id}>
|
return <g id={"spread-" + id}>
|
||||||
{!selected &&
|
{!selected &&
|
||||||
|
|
|
@ -21,7 +21,7 @@ function fakeFarmwares(): Dictionary<FarmwareManifest | undefined> {
|
||||||
args: ["my_farmware.fth"],
|
args: ["my_farmware.fth"],
|
||||||
url: "https://",
|
url: "https://",
|
||||||
path: "my_farmware",
|
path: "my_farmware",
|
||||||
config: [],
|
config: [{ name: "config_1", label: "Config 1", value: "4" }],
|
||||||
meta: {
|
meta: {
|
||||||
min_os_version_major: "3",
|
min_os_version_major: "3",
|
||||||
description: "Does things.",
|
description: "Does things.",
|
||||||
|
@ -35,19 +35,40 @@ function fakeFarmwares(): Dictionary<FarmwareManifest | undefined> {
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("<FarmwareForms/>", () => {
|
describe("<FarmwareForms/>", () => {
|
||||||
|
it("doesn't render", () => {
|
||||||
|
const farmwares = fakeFarmwares();
|
||||||
|
const farmware = farmwares.farmware_0;
|
||||||
|
if (farmware) { farmware.config = []; }
|
||||||
|
const wrapper = mount(<FarmwareForms
|
||||||
|
farmwares={farmwares}
|
||||||
|
user_env={{}} />);
|
||||||
|
expect(wrapper.text()).toEqual("");
|
||||||
|
});
|
||||||
|
|
||||||
it("renders", () => {
|
it("renders", () => {
|
||||||
const wrapper = mount(<FarmwareForms farmwares={fakeFarmwares()} />);
|
const wrapper = mount(<FarmwareForms
|
||||||
|
farmwares={fakeFarmwares()}
|
||||||
|
user_env={{}} />);
|
||||||
expect(wrapper.text()).toContain("My Farmware");
|
expect(wrapper.text()).toContain("My Farmware");
|
||||||
expect(wrapper.text()).toContain("version: 0.0.0");
|
expect(wrapper.text()).toContain("version: 0.0.0");
|
||||||
expect(wrapper.text()).toContain("Does things.");
|
expect(wrapper.text()).toContain("Does things.");
|
||||||
|
expect(wrapper.find("label").last().text()).toContain("Config 1");
|
||||||
|
expect(wrapper.find("input").props().value).toEqual("4");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("runs", () => {
|
it("runs", () => {
|
||||||
const runFarmware = getDevice().execScript as jest.Mock<{}>;
|
const runFarmware = getDevice().execScript as jest.Mock<{}>;
|
||||||
const wrapper = mount(<FarmwareForms farmwares={fakeFarmwares()} />);
|
const wrapper = mount(<FarmwareForms
|
||||||
|
farmwares={fakeFarmwares()}
|
||||||
|
user_env={{}} />);
|
||||||
const run = wrapper.find("button").first();
|
const run = wrapper.find("button").first();
|
||||||
run.simulate("click");
|
run.simulate("click");
|
||||||
expect(run.text()).toEqual("Run");
|
expect(run.text()).toEqual("Run");
|
||||||
expect(runFarmware).toHaveBeenCalledWith("My Farmware");
|
const argsList = runFarmware.mock.calls[0];
|
||||||
|
expect(argsList[0]).toEqual("My Farmware");
|
||||||
|
const pairs = argsList[1][0];
|
||||||
|
expect(pairs.kind).toEqual("pair");
|
||||||
|
expect(pairs.args)
|
||||||
|
.toEqual({ "label": "my_farmware_config_1", "value": "4" });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,6 +19,7 @@ describe("<FarmwarePage />", () => {
|
||||||
farmwares: {},
|
farmwares: {},
|
||||||
syncStatus: "unknown",
|
syncStatus: "unknown",
|
||||||
env: {},
|
env: {},
|
||||||
|
user_env: {},
|
||||||
dispatch: jest.fn(),
|
dispatch: jest.fn(),
|
||||||
currentImage: undefined,
|
currentImage: undefined,
|
||||||
images: []
|
images: []
|
||||||
|
|
|
@ -1,44 +1,63 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Widget, WidgetHeader, WidgetBody, Col } from "../ui/index";
|
import {
|
||||||
|
Widget, WidgetHeader, WidgetBody, Col, BlurableInput
|
||||||
|
} from "../ui/index";
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import { FarmwareManifest, Dictionary } from "farmbot";
|
import { FarmwareManifest, Dictionary, Pair, FarmwareConfig } from "farmbot";
|
||||||
import { betterCompact } from "../util";
|
import { betterCompact } from "../util";
|
||||||
import { getDevice } from "../device";
|
import { getDevice } from "../device";
|
||||||
|
import * as _ from "lodash";
|
||||||
|
|
||||||
interface FarmwareFormsProps {
|
interface FarmwareFormsProps {
|
||||||
farmwares: Dictionary<FarmwareManifest | undefined>;
|
farmwares: Dictionary<FarmwareManifest | undefined>;
|
||||||
|
user_env: Record<string, string | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: download and parse the "manifest.json" file instead.
|
|
||||||
const firstParty = [
|
|
||||||
"camera-calibration",
|
|
||||||
"historical-camera-calibration",
|
|
||||||
"take-photo",
|
|
||||||
"plant-detection",
|
|
||||||
"historical-plant-detection"];
|
|
||||||
|
|
||||||
export function FarmwareForms(props: FarmwareFormsProps): JSX.Element {
|
export function FarmwareForms(props: FarmwareFormsProps): JSX.Element {
|
||||||
const { farmwares } = props;
|
|
||||||
|
function inputChange(key: string, e: React.SyntheticEvent<HTMLInputElement>) {
|
||||||
|
const value = e.currentTarget.value;
|
||||||
|
getDevice().setUserEnv({ [key]: value });
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEnvName(farmwareName: string, configName: string) {
|
||||||
|
return `${_.snakeCase(farmwareName)}_${configName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getValue(farmwareName: string, currentConfig: FarmwareConfig) {
|
||||||
|
return (user_env[getEnvName(farmwareName, currentConfig.name)]
|
||||||
|
|| _.toString(currentConfig.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(farmwareName: string, config: FarmwareConfig[]) {
|
||||||
|
const pairs = config.map<Pair>((x) => {
|
||||||
|
const label = getEnvName(farmwareName, x.name);
|
||||||
|
const value = getValue(farmwareName, x);
|
||||||
|
return { kind: "pair", args: { value, label } };
|
||||||
|
});
|
||||||
|
getDevice().execScript(farmwareName, pairs);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { farmwares, user_env } = props;
|
||||||
const farmwareData = betterCompact(Object
|
const farmwareData = betterCompact(Object
|
||||||
.keys(farmwares)
|
.keys(farmwares)
|
||||||
.map(x => farmwares[x]))
|
.map(x => farmwares[x]))
|
||||||
.map((fw) => {
|
.map((fw) => {
|
||||||
// TODO: Add optional "config" field to farmbot-js and check for it here.
|
const needsWidget = fw.config && fw.config.length > 0;
|
||||||
const needsWidget = !firstParty.includes(fw.name);
|
|
||||||
return needsWidget ? fw : undefined;
|
return needsWidget ? fw : undefined;
|
||||||
});
|
});
|
||||||
return <div id="farmware-forms">
|
return <div id="farmware-forms">
|
||||||
{farmwareData.map((farmware, i) => {
|
{farmwareData.map((farmware, i) => {
|
||||||
return farmware ?
|
return farmware ?
|
||||||
<Col key={i} xs={12} sm={6}>
|
<Col key={i} xs={12} sm={6}>
|
||||||
<Widget>
|
<Widget className={_.kebabCase(farmware.name)}>
|
||||||
<WidgetHeader
|
<WidgetHeader
|
||||||
title={farmware.name}
|
title={farmware.name}
|
||||||
helpText={farmware.meta.version ? " version: "
|
helpText={farmware.meta.version ? " version: "
|
||||||
+ farmware.meta.version : ""}>
|
+ farmware.meta.version : ""}>
|
||||||
<button
|
<button
|
||||||
className="fb-button gray"
|
className="fb-button green"
|
||||||
onClick={() => getDevice().execScript(farmware.name)}>
|
onClick={() => run(farmware.name, farmware.config)}>
|
||||||
{t("Run")}
|
{t("Run")}
|
||||||
</button>
|
</button>
|
||||||
</WidgetHeader>
|
</WidgetHeader>
|
||||||
|
@ -47,8 +66,17 @@ export function FarmwareForms(props: FarmwareFormsProps): JSX.Element {
|
||||||
<div>
|
<div>
|
||||||
<label>Description</label>
|
<label>Description</label>
|
||||||
<p>{farmware.meta.description}</p>
|
<p>{farmware.meta.description}</p>
|
||||||
|
<hr />
|
||||||
</div>}
|
</div>}
|
||||||
{/* TODO: Render inputs described in "farmware.config". */}
|
{farmware.config.map((config) => {
|
||||||
|
return <div key={config.name} id={config.name}>
|
||||||
|
<label>{config.label}</label>
|
||||||
|
<BlurableInput type="text"
|
||||||
|
onCommit={(e) =>
|
||||||
|
inputChange(getEnvName(farmware.name, config.name), e)}
|
||||||
|
value={getValue(farmware.name, config)} />
|
||||||
|
</div>;
|
||||||
|
})}
|
||||||
</WidgetBody>
|
</WidgetBody>
|
||||||
</Widget>
|
</Widget>
|
||||||
</Col> : <div key={i} />;
|
</Col> : <div key={i} />;
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { CameraCalibration } from "./camera_calibration/camera_calibration";
|
||||||
import { FarmwareProps } from "../devices/interfaces";
|
import { FarmwareProps } from "../devices/interfaces";
|
||||||
import { WeedDetector } from "./weed_detector/index";
|
import { WeedDetector } from "./weed_detector/index";
|
||||||
import { envGet } from "./weed_detector/remote_env/selectors";
|
import { envGet } from "./weed_detector/remote_env/selectors";
|
||||||
// import { FarmwareForms } from "./farmware_forms";
|
import { FarmwareForms } from "./farmware_forms";
|
||||||
|
|
||||||
@connect(mapStateToProps)
|
@connect(mapStateToProps)
|
||||||
export class FarmwarePage extends React.Component<FarmwareProps, {}> {
|
export class FarmwarePage extends React.Component<FarmwareProps, {}> {
|
||||||
|
@ -48,7 +48,8 @@ export class FarmwarePage extends React.Component<FarmwareProps, {}> {
|
||||||
<WeedDetector {...this.props} />
|
<WeedDetector {...this.props} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{/* <FarmwareForms farmwares={this.props.farmwares} /> */}
|
<FarmwareForms farmwares={this.props.farmwares}
|
||||||
|
user_env={this.props.user_env} />
|
||||||
</Page>;
|
</Page>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ export function mapStateToProps(props: Everything): FarmwareProps {
|
||||||
farmwares,
|
farmwares,
|
||||||
syncStatus,
|
syncStatus,
|
||||||
env: prepopulateEnv(props.bot.hardware.user_env),
|
env: prepopulateEnv(props.bot.hardware.user_env),
|
||||||
|
user_env: props.bot.hardware.user_env,
|
||||||
dispatch: props.dispatch,
|
dispatch: props.dispatch,
|
||||||
currentImage,
|
currentImage,
|
||||||
images
|
images
|
||||||
|
|
|
@ -13,6 +13,7 @@ describe("<WeedDetector />", () => {
|
||||||
farmwares: {},
|
farmwares: {},
|
||||||
syncStatus: "unknown",
|
syncStatus: "unknown",
|
||||||
env: {},
|
env: {},
|
||||||
|
user_env: {},
|
||||||
dispatch: jest.fn(),
|
dispatch: jest.fn(),
|
||||||
currentImage: undefined,
|
currentImage: undefined,
|
||||||
images: []
|
images: []
|
||||||
|
|
|
@ -84,8 +84,8 @@ export class ImageWorkspace extends React.Component<Props, {}> {
|
||||||
onRelease={this.onHslChange("H")}
|
onRelease={this.onHslChange("H")}
|
||||||
lowest={RANGES.H.LOWEST}
|
lowest={RANGES.H.LOWEST}
|
||||||
highest={RANGES.H.HIGHEST}
|
highest={RANGES.H.HIGHEST}
|
||||||
lowValue={H_LO}
|
lowValue={Math.min(H_LO, H_HI)}
|
||||||
highValue={H_HI} />
|
highValue={Math.max(H_LO, H_HI)} />
|
||||||
<label htmlFor="saturation">{t("SATURATION")}</label>
|
<label htmlFor="saturation">{t("SATURATION")}</label>
|
||||||
<WeedDetectorSlider
|
<WeedDetectorSlider
|
||||||
onRelease={this.onHslChange("S")}
|
onRelease={this.onHslChange("S")}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Session } from "./session";
|
||||||
import { BooleanSetting } from "./session_keys";
|
import { BooleanSetting } from "./session_keys";
|
||||||
|
|
||||||
export function LoadingPlant() {
|
export function LoadingPlant() {
|
||||||
const animations = Session.getBool(BooleanSetting.plantAnimations);
|
const animations = !Session.getBool(BooleanSetting.disableAnimations);
|
||||||
return <div className="loading-plant-div-container">
|
return <div className="loading-plant-div-container">
|
||||||
<svg width="300px" height="500px">
|
<svg width="300px" height="500px">
|
||||||
{animations &&
|
{animations &&
|
||||||
|
|
|
@ -17,7 +17,7 @@ export enum BooleanSetting {
|
||||||
hideWebcamWidget = "hideWebcamWidget",
|
hideWebcamWidget = "hideWebcamWidget",
|
||||||
dynamicMap = "dynamicMap",
|
dynamicMap = "dynamicMap",
|
||||||
mapXL = "mapXL",
|
mapXL = "mapXL",
|
||||||
plantAnimations = "plantAnimations",
|
disableAnimations = "disableAnimations",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum NumericSetting {
|
export enum NumericSetting {
|
||||||
|
|
Loading…
Reference in New Issue