diff --git a/app/components/DeviceView.tsx b/app/components/DeviceView.tsx
index 6ba218f..6211bf9 100644
--- a/app/components/DeviceView.tsx
+++ b/app/components/DeviceView.tsx
@@ -1,8 +1,7 @@
import * as classNames from "classnames";
import { observer } from "mobx-react";
import * as React from "react";
-import FontAwesome = require("react-fontawesome");
-import { Header, Item } from "semantic-ui-react";
+import { Header, Icon, Item } from "semantic-ui-react";
import { injectState, MqttApiState } from "@app/state";
import { SprinklersDevice } from "@common/sprinklers";
@@ -16,7 +15,7 @@ const ConnectionState = ({ connected }: { connected: boolean }) => {
});
return (
-
+
{connected ? "Connected" : "Disconnected"}
);
@@ -51,10 +50,10 @@ class DeviceView extends React.Component {
Raspberry Pi Grinklers Instance
-
+
);
diff --git a/app/components/ProgramTable.tsx b/app/components/ProgramTable.tsx
index 0e290e5..4215477 100644
--- a/app/components/ProgramTable.tsx
+++ b/app/components/ProgramTable.tsx
@@ -1,6 +1,6 @@
import { observer } from "mobx-react";
import * as React from "react";
-import { Table } from "semantic-ui-react";
+import { Button, Table } from "semantic-ui-react";
import { Duration } from "@common/Duration";
import { Program, Schedule } from "@common/sprinklers";
@@ -27,11 +27,17 @@ export default class ProgramTable extends React.Component<{ programs: Program[]
Section {item.section + 1 + ""} for {duration.toString()}
);
});
+ const cancelOrRun = () => running ? program.cancel() : program.run();
return [(
{"" + (i + 1)}
{name}
- {running ? "Running" : "Not running"}
+
+ {running ? "Running" : "Not running"}
+
+
{enabled ? "Enabled" : "Not enabled"}
), (
diff --git a/app/components/SectionRunnerView.tsx b/app/components/SectionRunnerView.tsx
index 669b1c1..4763d5d 100644
--- a/app/components/SectionRunnerView.tsx
+++ b/app/components/SectionRunnerView.tsx
@@ -1,16 +1,55 @@
+import * as classNames from "classnames";
import { observer } from "mobx-react";
import * as React from "react";
-import { Segment } from "semantic-ui-react";
+import { Button, Icon, Segment } from "semantic-ui-react";
-import { SectionRunner } from "@common/sprinklers";
+import { Duration } from "@common/Duration";
+import { Section, SectionRun, SectionRunner } from "@common/sprinklers";
+
+function PausedState({ paused }: { paused: boolean }) {
+ const classes = classNames({
+ "sectionRunner--pausedState": true,
+ "sectionRunner--pausedState-paused": paused,
+ "sectionRunner--pausedState-unpaused": !paused,
+ });
+ return (
+
+
+ {paused ? "Paused" : "Processing"}
+
+ );
+}
+
+function SectionRunView({ run, sections }:
+ { run: SectionRun, sections: Section[] }) {
+ const section = sections[run.section];
+ const current = run.startTime != null;
+ const duration = Duration.fromSeconds(run.duration);
+ const cancel = run.cancel;
+ return (
+
+ '{section.name}' for {duration.toString()}
+
+
+ );
+}
@observer
-export default class SectionRunnerView extends React.Component<{ sectionRunner: SectionRunner }, {}> {
+export default class SectionRunnerView extends React.Component<{
+ sectionRunner: SectionRunner, sections: Section[],
+}, {}> {
render() {
+ const { current, queue, paused } = this.props.sectionRunner;
+ const { sections } = this.props;
+ const queueView = queue.map((run) =>
+ );
return (
- Section Runner Queue
- {this.props.sectionRunner.toString()}
+ Section Runner Queue
+
+ {current && }
+ {queueView}
+
);
}
diff --git a/app/components/SectionTable.tsx b/app/components/SectionTable.tsx
index 1a69ca8..77eb2b2 100644
--- a/app/components/SectionTable.tsx
+++ b/app/components/SectionTable.tsx
@@ -1,8 +1,7 @@
import * as classNames from "classnames";
import { observer } from "mobx-react";
import * as React from "react";
-import FontAwesome = require("react-fontawesome");
-import { Table } from "semantic-ui-react";
+import { Icon, Table } from "semantic-ui-react";
import { Section } from "@common/sprinklers";
@@ -21,7 +20,7 @@ export default class SectionTable extends React.Component<{ sections: Section[]
"section--state-false": !state,
});
const sectionState = state ?
- ( Irrigating)
+ ( Irrigating)
: "Not irrigating";
return (
diff --git a/app/styles/app.css b/app/styles/app.css
index 24ace86..a503e78 100644
--- a/app/styles/app.css
+++ b/app/styles/app.css
@@ -12,6 +12,20 @@
color: #D20000;
}
+.sectionRunner--pausedState {
+ padding-left: .75em;
+ font-size: .75em;
+ font-weight: lighter;
+}
+
+.sectionRunner--pausedState > .fa {
+ padding-right: .2em;
+}
+
+.sectionRunner--pausedState-unpaused {
+ color: #BBBBBB;
+}
+
.section--number,
.program--number {
width: 2em
diff --git a/common/sprinklers/SectionRunner.ts b/common/sprinklers/SectionRunner.ts
index e4d2d56..a05a90a 100644
--- a/common/sprinklers/SectionRunner.ts
+++ b/common/sprinklers/SectionRunner.ts
@@ -15,9 +15,7 @@ export class SectionRun {
this.section = section;
}
- cancel() {
- return this.sectionRunner.cancelRunById(this.id);
- }
+ cancel = () => this.sectionRunner.cancelRunById(this.id);
toString() {
return `SectionRun{id=${this.id}, section=${this.section}, duration=${this.duration},` +
@@ -40,6 +38,18 @@ export class SectionRunner {
return this.device.cancelSectionRunId({ runId });
}
+ setPaused(paused: boolean) {
+ return this.device.pauseSectionRunner({ paused });
+ }
+
+ pause() {
+ return this.setPaused(true);
+ }
+
+ unpause() {
+ return this.setPaused(false);
+ }
+
toString(): string {
return `SectionRunner{queue="${this.queue}", current="${this.current}", paused=${this.paused}}`;
}
diff --git a/common/sprinklers/schema/index.ts b/common/sprinklers/schema/index.ts
index 14c026c..c228126 100644
--- a/common/sprinklers/schema/index.ts
+++ b/common/sprinklers/schema/index.ts
@@ -21,7 +21,7 @@ export const section: ModelSchema = {
};
export const sectionRun: ModelSchema = {
- factory: (c) => new s.SectionRun(c.json.id),
+ factory: (c) => new s.SectionRun(c.parentContext.target, c.json.id),
props: {
id: primitive(),
section: primitive(),
diff --git a/common/sprinklers/schema/requests.ts b/common/sprinklers/schema/requests.ts
index a1f48b6..f44949e 100644
--- a/common/sprinklers/schema/requests.ts
+++ b/common/sprinklers/schema/requests.ts
@@ -1,4 +1,4 @@
-import { createSimpleSchema, deserialize, ModelSchema, object, primitive, serialize } from "serializr";
+import { createSimpleSchema, deserialize, ModelSchema, primitive, serialize } from "serializr";
import * as requests from "../requests";
import * as common from "./common";
@@ -16,9 +16,12 @@ export const withSection: ModelSchema = createSimpleSchema
sectionId: primitive(),
});
-export const updateProgramData: ModelSchema = createSimpleSchema({
+export const updateProgram: ModelSchema = createSimpleSchema({
...withProgram.props,
- data: object(createSimpleSchema({ "*": true })),
+ data: {
+ serializer: (data) => data,
+ deserializer: (json, done) => { done(null, json); },
+ },
});
export const runSection: ModelSchema = createSimpleSchema({
@@ -42,7 +45,7 @@ export function getRequestSchema(request: requests.WithType): ModelSchema {
case "cancelProgram":
return withProgram;
case "updateProgram":
- throw new Error("updateProgram not implemented");
+ return updateProgram;
case "runSection":
return runSection;
case "cancelSection":
diff --git a/package.json b/package.json
index d54714c..9d1da4c 100644
--- a/package.json
+++ b/package.json
@@ -56,7 +56,6 @@
"@types/prop-types": "^15.5.1",
"@types/react": "^16.0.10",
"@types/react-dom": "^16.0.1",
- "@types/react-fontawesome": "^1.5.0",
"@types/react-hot-loader": "^3.0.4",
"@types/webpack-env": "^1.13.0",
"@types/ws": "^3.2.0",
@@ -85,7 +84,6 @@
"react": "^16.0.0",
"react-dev-utils": "^4.1.0",
"react-dom": "^16.0.0",
- "react-fontawesome": "^1.6.1",
"react-hot-loader": "^3.0.0-beta.6",
"semantic-ui-css": "^2.2.10",
"semantic-ui-react": "^0.74.2",
diff --git a/server/index.ts b/server/index.ts
index 4d41429..afb9410 100644
--- a/server/index.ts
+++ b/server/index.ts
@@ -54,7 +54,7 @@ async function deviceCallRequest(socket: WebSocket, data: ws.IDeviceCallRequest)
function webSocketHandler(socket: WebSocket) {
const stop = autorunAsync(() => {
const json = serialize(schema.sprinklersDevice, device);
- log.info({ device: json });
+ log.trace({ device: json });
const data = { type: "deviceUpdate", name: "grinklers", data: json };
socket.send(JSON.stringify(data));
}, 100);
diff --git a/yarn.lock b/yarn.lock
index 25ca78d..5d8c49e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -70,12 +70,6 @@
"@types/node" "*"
"@types/react" "*"
-"@types/react-fontawesome@^1.5.0":
- version "1.6.1"
- resolved "https://registry.yarnpkg.com/@types/react-fontawesome/-/react-fontawesome-1.6.1.tgz#a07ff96f89c9a778cc7abb8e66b52f0c47bb3188"
- dependencies:
- "@types/react" "*"
-
"@types/react-hot-loader@^3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@types/react-hot-loader/-/react-hot-loader-3.0.4.tgz#7fc081509830c64218d8a99a865e2fb4a94572ad"
@@ -4432,7 +4426,7 @@ promise@^8.0.1:
dependencies:
asap "~2.0.3"
-prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.6.0:
+prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.6.0:
version "15.6.0"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856"
dependencies:
@@ -4619,12 +4613,6 @@ react-error-overlay@^2.0.2:
settle-promise "1.0.0"
source-map "0.5.6"
-react-fontawesome@^1.6.1:
- version "1.6.1"
- resolved "https://registry.yarnpkg.com/react-fontawesome/-/react-fontawesome-1.6.1.tgz#eddce17e7dc731aa09fd4a186688a61793a16c5c"
- dependencies:
- prop-types "^15.5.6"
-
react-hot-loader@^3.0.0-beta.6:
version "3.0.0-beta.7"
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-3.0.0-beta.7.tgz#d5847b8165d731c4d5b30d86d5d4716227a0fa83"