Fixed/improved a bunch of stuff
This commit is contained in:
parent
652fe5b036
commit
d895e2e3e9
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
39
.vscode/tasks.json
vendored
39
.vscode/tasks.json
vendored
@ -1,27 +1,28 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "0.1.0",
|
||||
"command": "npm",
|
||||
"isShellCommand": true,
|
||||
"showOutput": "always",
|
||||
"suppressTaskName": true,
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"taskName": "install",
|
||||
"args": ["install"]
|
||||
},
|
||||
{
|
||||
"taskName": "update",
|
||||
"args": ["update"]
|
||||
},
|
||||
{
|
||||
"taskName": "start",
|
||||
"args": ["run", "start"]
|
||||
},
|
||||
{
|
||||
"taskName": "test",
|
||||
"args": ["run", "test"]
|
||||
"type": "npm",
|
||||
"script": "start",
|
||||
"problemMatcher": {
|
||||
"owner": "webpack",
|
||||
"severity": "error",
|
||||
"fileLocation": "relative",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "ERROR in (.*)",
|
||||
"file": 1
|
||||
},
|
||||
{
|
||||
"regexp": "\\((\\d+),(\\d+)\\):(.*)",
|
||||
"line": 1,
|
||||
"column": 2,
|
||||
"message": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,19 +1,19 @@
|
||||
import * as classNames from "classnames";
|
||||
import {observer} from "mobx-react";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import {Header, Item} from "semantic-ui-react";
|
||||
import {ProgramTable, RunSectionForm, SectionTable, SectionRunnerView} from ".";
|
||||
import { Header, Item } from "semantic-ui-react";
|
||||
import { ProgramTable, RunSectionForm, SectionRunnerView, SectionTable } from ".";
|
||||
|
||||
import {SprinklersDevice} from "../sprinklers";
|
||||
import { SprinklersDevice } from "../sprinklers";
|
||||
import FontAwesome = require("react-fontawesome");
|
||||
|
||||
const ConnectionState = ({connected}: { connected: boolean }) =>
|
||||
const ConnectionState = ({ connected }: { connected: boolean }) =>
|
||||
<span className={classNames({
|
||||
"device--connectionState": true,
|
||||
"device--connectionState-connected": connected,
|
||||
"device--connectionState-disconnected": !connected,
|
||||
})}>
|
||||
<FontAwesome name={connected ? "plug" : "chain-broken"}/>
|
||||
<FontAwesome name={connected ? "plug" : "chain-broken"} />
|
||||
|
||||
{connected ? "Connected" : "Disconnected"}
|
||||
</span>;
|
||||
@ -21,22 +21,22 @@ const ConnectionState = ({connected}: { connected: boolean }) =>
|
||||
@observer
|
||||
export default class DeviceView extends React.PureComponent<{ device: SprinklersDevice }, {}> {
|
||||
render() {
|
||||
const {id, connected, sections, programs, sectionRunner} = this.props.device;
|
||||
const { id, connected, sections, programs, sectionRunner } = this.props.device;
|
||||
return (
|
||||
<Item>
|
||||
<Item.Image src={require<string>("app/images/raspberry_pi.png")}/>
|
||||
<Item.Image src={require<string>("app/images/raspberry_pi.png")} />
|
||||
<Item.Content>
|
||||
<Header as="h1">
|
||||
<span>Device </span><kbd>{id}</kbd>
|
||||
<ConnectionState connected={connected}/>
|
||||
<ConnectionState connected={connected} />
|
||||
</Header>
|
||||
<Item.Meta>
|
||||
|
||||
</Item.Meta>
|
||||
<SectionRunnerView sectionRunner={sectionRunner}/>
|
||||
<SectionTable sections={sections}/>
|
||||
<RunSectionForm sections={sections}/>
|
||||
<ProgramTable programs={programs}/>
|
||||
<SectionRunnerView sectionRunner={sectionRunner} />
|
||||
<SectionTable sections={sections} />
|
||||
<RunSectionForm sections={sections} />
|
||||
<ProgramTable programs={programs} />
|
||||
</Item.Content>
|
||||
</Item>
|
||||
);
|
||||
|
@ -1,11 +1,11 @@
|
||||
import * as React from "react";
|
||||
import {Input} from "semantic-ui-react";
|
||||
import {Duration} from "../sprinklers";
|
||||
import { Input, InputOnChangeData } from "semantic-ui-react";
|
||||
import { Duration } from "../sprinklers";
|
||||
|
||||
export default class DurationInput extends React.Component<{
|
||||
duration: Duration,
|
||||
onDurationChange?: (newDuration: Duration) => void;
|
||||
}, {}> {
|
||||
onDurationChange: (newDuration: Duration) => void;
|
||||
}> {
|
||||
render() {
|
||||
const duration = this.props.duration;
|
||||
// const editing = this.props.onDurationChange != null;
|
||||
@ -13,25 +13,25 @@ export default class DurationInput extends React.Component<{
|
||||
<label>Duration</label>
|
||||
<div className="fields">
|
||||
<Input type="number" className="field durationInput--minutes"
|
||||
value={duration.minutes} onChange={this.onMinutesChange}
|
||||
label="M" labelPosition="right"/>
|
||||
value={duration.minutes} onChange={this.onMinutesChange}
|
||||
label="M" labelPosition="right" />
|
||||
<Input type="number" className="field durationInput--seconds"
|
||||
value={duration.seconds} onChange={this.onSecondsChange} max="60"
|
||||
label="S" labelPosition="right"/>
|
||||
value={duration.seconds} onChange={this.onSecondsChange} max="60"
|
||||
label="S" labelPosition="right" />
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
private onMinutesChange = (e, {value}) => {
|
||||
if (value.length === 0 || isNaN(value)) {
|
||||
private onMinutesChange = (e: React.SyntheticEvent<any>, { value }: InputOnChangeData) => {
|
||||
if (value.length === 0 || isNaN(Number(value))) {
|
||||
return;
|
||||
}
|
||||
const newMinutes = parseInt(value, 10);
|
||||
this.props.onDurationChange(this.props.duration.withMinutes(newMinutes));
|
||||
}
|
||||
|
||||
private onSecondsChange = (e, {value}) => {
|
||||
if (value.length === 0 || isNaN(value)) {
|
||||
private onSecondsChange = (e: React.SyntheticEvent<any>, { value }: InputOnChangeData) => {
|
||||
if (value.length === 0 || isNaN(Number(value))) {
|
||||
return;
|
||||
}
|
||||
const newSeconds = parseInt(value, 10);
|
||||
|
@ -14,7 +14,7 @@ export class ScheduleView extends React.PureComponent<{ schedule: Schedule }, {}
|
||||
|
||||
@observer
|
||||
export default class ProgramTable extends React.PureComponent<{ programs: Program[] }, {}> {
|
||||
private static renderRow(program: Program, i: number): JSX.Element[] {
|
||||
private static renderRows(program: Program, i: number): JSX.Element[] | null {
|
||||
if (!program) {
|
||||
return null;
|
||||
}
|
||||
@ -57,7 +57,7 @@ export default class ProgramTable extends React.PureComponent<{ programs: Progra
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{
|
||||
Array.prototype.concat.apply([], this.props.programs.map(ProgramTable.renderRow))
|
||||
Array.prototype.concat.apply([], this.props.programs.map(ProgramTable.renderRows))
|
||||
}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {computed} from "mobx";
|
||||
import {observer} from "mobx-react";
|
||||
import * as React from "react";
|
||||
import {SyntheticEvent} from "react";
|
||||
import {DropdownItemProps, DropdownProps, Form, Header, Segment} from "semantic-ui-react";
|
||||
import {DurationInput} from ".";
|
||||
import {Duration, Section} from "../sprinklers";
|
||||
@ -37,7 +36,7 @@ export default class RunSectionForm extends React.Component<{
|
||||
</Segment>;
|
||||
}
|
||||
|
||||
private onSectionChange = (e: SyntheticEvent<HTMLElement>, v: DropdownProps) => {
|
||||
private onSectionChange = (e: React.SyntheticEvent<HTMLElement>, v: DropdownProps) => {
|
||||
this.setState({section: v.value as number});
|
||||
}
|
||||
|
||||
@ -45,10 +44,13 @@ export default class RunSectionForm extends React.Component<{
|
||||
this.setState({duration: newDuration});
|
||||
}
|
||||
|
||||
private run = (e: SyntheticEvent<HTMLElement>) => {
|
||||
private run = (e: React.SyntheticEvent<HTMLElement>) => {
|
||||
e.preventDefault();
|
||||
if (typeof this.state.section !== "number") {
|
||||
return;
|
||||
}
|
||||
const section: Section = this.props.sections[this.state.section];
|
||||
console.log(`should run section ${section} for ${this.state.duration}`);
|
||||
console.log(`running section ${section} for ${this.state.duration}`);
|
||||
section.run(this.state.duration)
|
||||
.then((a) => console.log("ran section", a))
|
||||
.catch((err) => console.error("error running section", err));
|
||||
|
@ -1,10 +1,10 @@
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import {AppContainer} from "react-hot-loader";
|
||||
import { AppContainer } from "react-hot-loader";
|
||||
|
||||
import App from "./components/App";
|
||||
import {MqttApiClient} from "./mqtt";
|
||||
import {Message, UiStore} from "./ui";
|
||||
import { MqttApiClient } from "./mqtt";
|
||||
import { Message, UiStore } from "./ui";
|
||||
|
||||
const client = new MqttApiClient();
|
||||
client.start();
|
||||
@ -15,14 +15,14 @@ uiStore.addMessage(new Message("asdf", "boo!", Message.Type.Error));
|
||||
const rootElem = document.getElementById("app");
|
||||
|
||||
ReactDOM.render(<AppContainer>
|
||||
<App device={device} uiStore={uiStore}/>
|
||||
<App device={device} uiStore={uiStore} />
|
||||
</AppContainer>, rootElem);
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept("./components/App", () => {
|
||||
const NextApp = require<any>("./components/App").default as typeof App;
|
||||
ReactDOM.render(<AppContainer>
|
||||
<NextApp device={device} uiStore={uiStore}/>
|
||||
<NextApp device={device} uiStore={uiStore} />
|
||||
</AppContainer>, rootElem);
|
||||
});
|
||||
}
|
||||
|
@ -1,26 +1,26 @@
|
||||
import {EventEmitter} from "events";
|
||||
import { EventEmitter } from "events";
|
||||
import "paho-mqtt";
|
||||
import {
|
||||
Duration,
|
||||
ISectionRun,
|
||||
ISprinklersApi,
|
||||
ITimeOfDay,
|
||||
Program,
|
||||
ProgramItem,
|
||||
Schedule,
|
||||
Section,
|
||||
SectionRun,
|
||||
SectionRunner,
|
||||
SprinklersDevice,
|
||||
TimeOfDay,
|
||||
} from "./sprinklers";
|
||||
import {checkedIndexOf} from "./utils";
|
||||
import { checkedIndexOf } from "./utils";
|
||||
import MQTT = Paho.MQTT;
|
||||
|
||||
export class MqttApiClient extends EventEmitter implements ISprinklersApi {
|
||||
export class MqttApiClient implements ISprinklersApi {
|
||||
client: MQTT.Client;
|
||||
connected: boolean;
|
||||
devices: { [prefix: string]: MqttSprinklersDevice } = {};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.client = new MQTT.Client(location.hostname, 1884, MqttApiClient.newClientId());
|
||||
this.client.onMessageArrived = (m) => this.onMessageArrived(m);
|
||||
this.client.onConnectionLost = (e) => this.onConnectionLost(e);
|
||||
@ -80,6 +80,10 @@ export class MqttApiClient extends EventEmitter implements ISprinklersApi {
|
||||
|
||||
private processMessage(m: MQTT.Message) {
|
||||
// console.log("message arrived: ", m);
|
||||
if (m.destinationName == null) {
|
||||
console.warn(`revieved invalid message: ${m}`);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
@ -130,7 +134,7 @@ class MqttSprinklersDevice extends SprinklersDevice {
|
||||
doSubscribe() {
|
||||
const c = this.apiClient.client;
|
||||
this.subscriptions
|
||||
.forEach((filter) => c.subscribe(filter, {qos: 1}));
|
||||
.forEach((filter) => c.subscribe(filter, { qos: 1 }));
|
||||
}
|
||||
|
||||
doUnsubscribe() {
|
||||
@ -186,7 +190,7 @@ class MqttSprinklersDevice extends SprinklersDevice {
|
||||
}
|
||||
matches = topic.match(/^section_runner$/);
|
||||
if (matches != null) {
|
||||
(this.sectionRunner as MqttSectionRunner).onMessage(null, payload);
|
||||
(this.sectionRunner as MqttSectionRunner).onMessage(payload);
|
||||
return;
|
||||
}
|
||||
matches = topic.match(/^responses\/(\d+)$/);
|
||||
@ -219,7 +223,15 @@ class MqttSprinklersDevice extends SprinklersDevice {
|
||||
}
|
||||
|
||||
cancelSectionRunById(id: number) {
|
||||
return this.makeRequest(`section_runner/cancel_id`, {id});
|
||||
return this.makeRequest(`section_runner/cancel_id`, { id });
|
||||
}
|
||||
|
||||
pauseSectionRunner() {
|
||||
return this.makeRequest(`section_runner/pause`);
|
||||
}
|
||||
|
||||
unpauseSectionRunner() {
|
||||
return this.makeRequest(`section_runner/unpause`);
|
||||
}
|
||||
|
||||
//noinspection JSMethodCanBeStatic
|
||||
@ -227,7 +239,7 @@ class MqttSprinklersDevice extends SprinklersDevice {
|
||||
return Math.floor(Math.random() * 1000000000);
|
||||
}
|
||||
|
||||
private makeRequest(topic: string, payload: object | string): Promise<IResponseData> {
|
||||
private makeRequest(topic: string, payload: object | string = {}): Promise<IResponseData> {
|
||||
return new Promise<IResponseData>((resolve, reject) => {
|
||||
const payloadStr = (typeof payload === "string") ?
|
||||
payload : JSON.stringify(payload);
|
||||
@ -254,7 +266,7 @@ interface IResponseData {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
type ResponseCallback = (IResponseData) => void;
|
||||
type ResponseCallback = (data: IResponseData) => void;
|
||||
|
||||
interface ISectionJSON {
|
||||
name: string;
|
||||
@ -276,8 +288,19 @@ class MqttSection extends Section {
|
||||
}
|
||||
}
|
||||
|
||||
interface ITimeOfDayJSON {
|
||||
hour: number;
|
||||
minute: number;
|
||||
second: number;
|
||||
millisecond: number;
|
||||
}
|
||||
|
||||
function timeOfDayFromJSON(json: ITimeOfDayJSON): TimeOfDay {
|
||||
return new TimeOfDay(json.hour, json.minute, json.second, json.millisecond);
|
||||
}
|
||||
|
||||
interface IScheduleJSON {
|
||||
times: ITimeOfDay[];
|
||||
times: ITimeOfDayJSON[];
|
||||
weekdays: number[];
|
||||
from?: string;
|
||||
to?: string;
|
||||
@ -285,7 +308,7 @@ interface IScheduleJSON {
|
||||
|
||||
function scheduleFromJSON(json: IScheduleJSON): Schedule {
|
||||
const sched = new Schedule();
|
||||
sched.times = json.times;
|
||||
sched.times = json.times.map(timeOfDayFromJSON);
|
||||
sched.weekdays = json.weekdays;
|
||||
sched.from = json.from == null ? null : new Date(json.from);
|
||||
sched.to = json.to == null ? null : new Date(json.to);
|
||||
@ -322,11 +345,10 @@ class MqttProgram extends Program {
|
||||
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),
|
||||
}));
|
||||
this.sequence = json.sequence.map((item) => (new ProgramItem(
|
||||
item.section,
|
||||
Duration.fromSeconds(item.duration),
|
||||
)));
|
||||
}
|
||||
if (json.sched != null) {
|
||||
this.schedule = scheduleFromJSON(json.sched);
|
||||
@ -334,23 +356,42 @@ class MqttProgram extends Program {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ISectionRunJSON {
|
||||
id: number;
|
||||
section: number;
|
||||
duration: number;
|
||||
startTime?: number;
|
||||
pauseTime?: number;
|
||||
}
|
||||
|
||||
function sectionRunFromJSON(json: ISectionRunJSON) {
|
||||
const run = new SectionRun();
|
||||
run.id = json.id;
|
||||
run.section = json.section;
|
||||
run.duration = Duration.fromSeconds(json.duration);
|
||||
run.startTime = json.startTime == null ? null : new Date(json.startTime);
|
||||
run.pauseTime = json.pauseTime == null ? null : new Date(json.pauseTime);
|
||||
return run;
|
||||
}
|
||||
|
||||
interface ISectionRunnerJSON {
|
||||
queue: ISectionRun[];
|
||||
current?: ISectionRun;
|
||||
queue: ISectionRunJSON[];
|
||||
current: ISectionRunJSON | null;
|
||||
paused: boolean;
|
||||
}
|
||||
|
||||
class MqttSectionRunner extends SectionRunner {
|
||||
onMessage(topic: string, payload: string) {
|
||||
onMessage(payload: string) {
|
||||
const json = JSON.parse(payload) as ISectionRunnerJSON;
|
||||
this.updateFromJSON(json);
|
||||
}
|
||||
|
||||
updateFromJSON(json: ISectionRunnerJSON) {
|
||||
if (!json.queue) { // null means empty queue
|
||||
if (!json.queue || !json.queue.length) { // null means empty queue
|
||||
this.queue.clear();
|
||||
} else {
|
||||
this.queue.replace(json.queue);
|
||||
this.queue.replace(json.queue.map(sectionRunFromJSON));
|
||||
}
|
||||
this.current = json.current;
|
||||
this.current = json.current == null ? null : sectionRunFromJSON(json.current);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {IObservableArray, observable} from "mobx";
|
||||
import { IObservableArray, observable } from "mobx";
|
||||
|
||||
export abstract class Section {
|
||||
device: SprinklersDevice;
|
||||
@ -22,11 +22,22 @@ export abstract class Section {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ITimeOfDay {
|
||||
export class TimeOfDay {
|
||||
hour: number;
|
||||
minute: number;
|
||||
second: number;
|
||||
millisecond: number;
|
||||
|
||||
constructor(hour: number, minute: number = 0, second: number = 0, millisecond: number = 0) {
|
||||
this.hour = hour;
|
||||
this.minute = minute;
|
||||
this.second = second;
|
||||
this.millisecond = millisecond;
|
||||
}
|
||||
|
||||
static fromDate(date: Date): TimeOfDay {
|
||||
return new TimeOfDay(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
|
||||
}
|
||||
}
|
||||
|
||||
export enum Weekday {
|
||||
@ -34,17 +45,17 @@ export enum Weekday {
|
||||
}
|
||||
|
||||
export class Schedule {
|
||||
times: ITimeOfDay[] = [];
|
||||
times: TimeOfDay[] = [];
|
||||
weekdays: Weekday[] = [];
|
||||
from?: Date = null;
|
||||
to?: Date = null;
|
||||
from: Date | null = null;
|
||||
to: Date | null = null;
|
||||
}
|
||||
|
||||
export class Duration {
|
||||
minutes: number = 0;
|
||||
seconds: number = 0;
|
||||
|
||||
constructor(minutes: number, seconds: number) {
|
||||
constructor(minutes: number = 0, seconds: number = 0) {
|
||||
this.minutes = minutes;
|
||||
this.seconds = seconds;
|
||||
}
|
||||
@ -82,11 +93,16 @@ export class Duration {
|
||||
}
|
||||
}
|
||||
|
||||
export interface IProgramItem {
|
||||
export class ProgramItem {
|
||||
// the section number
|
||||
section: number;
|
||||
// duration of the run
|
||||
duration: Duration;
|
||||
|
||||
constructor(section: number, duration: Duration) {
|
||||
this.section = section;
|
||||
this.duration = duration;
|
||||
}
|
||||
}
|
||||
|
||||
export class Program {
|
||||
@ -101,7 +117,7 @@ export class Program {
|
||||
schedule: Schedule = new Schedule();
|
||||
|
||||
@observable
|
||||
sequence: IProgramItem[] = [];
|
||||
sequence: ProgramItem[] = [];
|
||||
|
||||
@observable
|
||||
running: boolean = false;
|
||||
@ -120,21 +136,38 @@ export class Program {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ISectionRun {
|
||||
export class SectionRun {
|
||||
id: number;
|
||||
section: number;
|
||||
duration: number;
|
||||
startTime?: Date;
|
||||
duration: Duration;
|
||||
startTime: Date | null;
|
||||
pauseTime: Date | null;
|
||||
|
||||
constructor(id: number = 0, section: number = 0, duration: Duration = new Duration()) {
|
||||
this.id = id;
|
||||
this.section = section;
|
||||
this.duration = duration;
|
||||
this.startTime = null;
|
||||
this.pauseTime = null;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `SectionRun{id=${this.id}, section=${this.section}, duration=${this.duration},` +
|
||||
` startTime=${this.startTime}, pauseTime=${this.pauseTime}}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class SectionRunner {
|
||||
device: SprinklersDevice;
|
||||
|
||||
@observable
|
||||
queue: IObservableArray<ISectionRun> = observable([]);
|
||||
queue: IObservableArray<SectionRun> = observable([]);
|
||||
|
||||
@observable
|
||||
current: ISectionRun = null;
|
||||
current: SectionRun | null = null;
|
||||
|
||||
@observable
|
||||
paused: boolean = false;
|
||||
|
||||
constructor(device: SprinklersDevice) {
|
||||
this.device = device;
|
||||
@ -145,7 +178,7 @@ export class SectionRunner {
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `SectionRunner{queue="${this.queue}", current="${this.current}"}`;
|
||||
return `SectionRunner{queue="${this.queue}", current="${this.current}", paused=${this.paused}}`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,10 +187,10 @@ export abstract class SprinklersDevice {
|
||||
connected: boolean = false;
|
||||
|
||||
@observable
|
||||
sections: IObservableArray<Section> = [] as IObservableArray<Section>;
|
||||
sections: IObservableArray<Section> = observable.array<Section>();
|
||||
|
||||
@observable
|
||||
programs: IObservableArray<Program> = [] as IObservableArray<Program>;
|
||||
programs: IObservableArray<Program> = observable.array<Program>();
|
||||
|
||||
@observable
|
||||
sectionRunner: SectionRunner;
|
||||
@ -169,12 +202,16 @@ export abstract class SprinklersDevice {
|
||||
abstract runProgram(program: number | Program): Promise<{}>;
|
||||
|
||||
abstract cancelSectionRunById(id: number): Promise<{}>;
|
||||
|
||||
abstract pauseSectionRunner(): Promise<{}>;
|
||||
|
||||
abstract unpauseSectionRunner(): Promise<{}>;
|
||||
}
|
||||
|
||||
export interface ISprinklersApi {
|
||||
start();
|
||||
start(): void;
|
||||
|
||||
getDevice(id: string): SprinklersDevice;
|
||||
|
||||
removeDevice(id: string);
|
||||
removeDevice(id: string): void;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
"clean": "rm -rf ./dist ./build",
|
||||
"build": "webpack --config ./webpack/prod.config.js",
|
||||
"start": "webpack-dev-server --config ./webpack/dev.config.js",
|
||||
"lint": "tslint \"app/script/**/*\" --force"
|
||||
"lint": "tslint --project . --force"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -29,6 +29,7 @@
|
||||
"@types/react": "^16",
|
||||
"@types/react-dom": "^15.5.0",
|
||||
"@types/react-fontawesome": "^1.5.0",
|
||||
"@types/react-hot-loader": "^3.0.4",
|
||||
"@types/react-transition-group": "^1.1.1",
|
||||
"classnames": "^2.2.5",
|
||||
"core-js": "^2.4.1",
|
||||
|
@ -5,7 +5,8 @@
|
||||
"experimentalDecorators": true,
|
||||
"target": "es5",
|
||||
"lib": ["es6", "dom"],
|
||||
"typeRoots": ["node_modules/@types"]
|
||||
"typeRoots": ["node_modules/@types"],
|
||||
"strict": true
|
||||
},
|
||||
"files": [
|
||||
"./node_modules/@types/webpack-env/index.d.ts",
|
||||
|
@ -26,6 +26,13 @@
|
||||
{
|
||||
"order": "fields-first"
|
||||
}
|
||||
],
|
||||
"object-literal-sort-keys": [
|
||||
false
|
||||
],
|
||||
"no-submodule-imports": false,
|
||||
"no-unused-variable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"rulesDirectory": []
|
||||
|
@ -34,6 +34,12 @@
|
||||
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"
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-transition-group@^1.1.1":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-1.1.2.tgz#a349e70788a6dc723a5f439217011ef926c27b4f"
|
||||
|
Loading…
x
Reference in New Issue
Block a user