From 15bd1ebebb51837f2ff171591ac6bd6dabcf4c97 Mon Sep 17 00:00:00 2001 From: Alex Mikhalev Date: Sun, 7 May 2017 23:30:36 -0600 Subject: [PATCH] Fixed tslint things and worked more on the ui --- app/script/App.tsx | 43 +++++++++++++++++++++++++-------- app/script/mqtt.ts | 52 ++++++++++++++++++++++++++++------------ app/script/sprinklers.ts | 22 +++++++++++++++-- app/style/app.css | 9 +++++++ package.json | 1 + 5 files changed, 100 insertions(+), 27 deletions(-) diff --git a/app/script/App.tsx b/app/script/App.tsx index aa9cc33..45325aa 100644 --- a/app/script/App.tsx +++ b/app/script/App.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import { computed } from "mobx"; import DevTools from "mobx-react-devtools"; import { observer } from "mobx-react"; -import { SprinklersDevice, Section, Program } from "./sprinklers"; +import { SprinklersDevice, Section, Program, Duration } from "./sprinklers"; import { Item, Table, Header, Segment, Form, Input, DropdownItemProps } from "semantic-ui-react"; import FontAwesome = require("react-fontawesome"); import * as classNames from "classnames"; @@ -16,6 +16,9 @@ import "app/style/app.css"; @observer class SectionTable extends React.PureComponent<{ sections: Section[] }, void> { private static renderRow(section: Section, index: number) { + if (!section) { + return null; + } const { name, state } = section; return ( @@ -55,6 +58,25 @@ class SectionTable extends React.PureComponent<{ sections: Section[] }, void> { } } +class DurationInput extends React.Component<{ + duration: Duration, + onDurationChange?: (newDuration: Duration) => void; +}, void> { + public render() { + const duration = this.props.duration; + return
+ +
+ + : + +
+
; + } +} + @observer class RunSectionForm extends React.Component<{ sections: Section[] }, void> { public render() { @@ -63,26 +85,27 @@ class RunSectionForm extends React.Component<{ sections: Section[] }, void> {
- +
; } - @computed private get sectionOptions(): DropdownItemProps[] { - // return this.props.sections.map((s, i) => ({ - // text: s.name, - // value: i, - // })); - return []; + return this.props.sections.map((s, i) => ({ + text: s ? s.name : null, + value: i, + })); } } @observer class ProgramTable extends React.PureComponent<{ programs: Program[] }, void> { private static renderRow(program: Program, i: number) { + if (!program) { + return null; + } const { name, running } = program; return ( @@ -155,8 +178,8 @@ class DeviceView extends React.PureComponent<{ device: SprinklersDevice }, void> export default class App extends React.PureComponent<{ device: SprinklersDevice }, any> { public render() { return - - + + ; } } diff --git a/app/script/mqtt.ts b/app/script/mqtt.ts index 2367b96..df3a31a 100644 --- a/app/script/mqtt.ts +++ b/app/script/mqtt.ts @@ -4,7 +4,7 @@ import MQTT = Paho.MQTT; import { EventEmitter } from "events"; import * as objectAssign from "object-assign"; import { - SprinklersDevice, ISprinklersApi, Section, Program, IProgramItem, Schedule, ITimeOfDay, Weekday, + SprinklersDevice, ISprinklersApi, Section, Program, IProgramItem, Schedule, ITimeOfDay, Weekday, Duration, } from "./sprinklers"; export class MqttApiClient extends EventEmitter implements ISprinklersApi { @@ -23,6 +23,7 @@ export class MqttApiClient extends EventEmitter implements ISprinklersApi { this.client = new MQTT.Client(location.hostname, 1884, MqttApiClient.newClientId()); this.client.onMessageArrived = (m) => this.onMessageArrived(m); this.client.onConnectionLost = (e) => this.onConnectionLost(e); + // (this.client as any).trace = (m => console.log(m)); } public start() { @@ -65,7 +66,15 @@ export class MqttApiClient extends EventEmitter implements ISprinklersApi { } private onMessageArrived(m: MQTT.Message) { - console.log("message arrived: ", m); + try { + this.processMessage(m); + } catch (e) { + console.error("error while processing mqtt message", e); + } + } + + private processMessage(m: MQTT.Message) { + // console.log("message arrived: ", m); const topicIdx = m.destinationName.indexOf("/"); // find the first / const prefix = m.destinationName.substr(0, topicIdx); // assume prefix does not contain a / const topic = m.destinationName.substr(topicIdx + 1); @@ -197,10 +206,15 @@ function scheduleFromJSON(json: IScheduleJSON): Schedule { return sched; } +interface IProgramItemJSON { + section: number; + duration: number; +} + interface IProgramJSON { name: string; enabled: boolean; - sequence: IProgramItem[]; + sequence: IProgramItemJSON[]; sched: IScheduleJSON; } @@ -210,18 +224,26 @@ class MqttProgram extends Program { this.running = (payload === "true"); } else if (topic == null) { const json = JSON.parse(payload) as Partial; - if (json.name != null) { - this.name = json.name; - } - if (json.enabled != null) { - this.enabled = json.enabled; - } - if (json.sequence != null) { - this.sequence = json.sequence; - } - if (json.sched != null) { - this.schedule = scheduleFromJSON(json.sched); - } + this.updateFromJSON(json); + } + } + + public updateFromJSON(json: Partial) { + if (json.name != null) { + this.name = json.name; + } + if (json.enabled != null) { + this.enabled = json.enabled; + } + if (json.sequence != null) { + // tslint:disable:object-literal-sort-keys + this.sequence = json.sequence.map((item) => ({ + section: item.section, + duration: Duration.fromSeconds(item.duration), + })); + } + if (json.sched != null) { + this.schedule = scheduleFromJSON(json.sched); } } } diff --git a/app/script/sprinklers.ts b/app/script/sprinklers.ts index e114cf7..d80fed0 100644 --- a/app/script/sprinklers.ts +++ b/app/script/sprinklers.ts @@ -26,11 +26,29 @@ export class Schedule { public to?: Date = null; } +export class Duration { + public static fromSeconds(seconds: number): Duration { + return new Duration(Math.floor(seconds / 60), seconds % 60); + } + + public minutes: number = 0; + public seconds: number = 0; + + constructor(minutes: number, seconds: number) { + this.minutes = minutes; + this.seconds = seconds; + } + + public toSeconds(): number { + return this.minutes * 60 + this.seconds; + } +} + export interface IProgramItem { // the section number section: number; - // duration in seconds - duration: number; + // duration of the run + duration: Duration; } export class Program { diff --git a/app/style/app.css b/app/style/app.css index 3ce7729..b564f42 100644 --- a/app/style/app.css +++ b/app/style/app.css @@ -33,4 +33,13 @@ .section--state-false { +} + +.durationInput--minutes, +.durationInput--seconds { + width: 100px; +} + +.durationInput--colon { + line-height: 38px; } \ No newline at end of file diff --git a/package.json b/package.json index ae1d132..e480b31 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "source-map-loader": "^0.2.1", "style-loader": "^0.17.0", "ts-loader": "^2.0.3", + "tslint": "^5.2.0", "typescript": "^2.3.1", "webpack": "^2.4.1", "webpack-dev-server": "^2.4.4"