Lots of good improvements
This commit is contained in:
parent
7846653297
commit
adffea950a
5
.vscode/tasks.json
vendored
5
.vscode/tasks.json
vendored
@ -48,6 +48,11 @@
|
||||
"type": "npm",
|
||||
"script": "start:dev-server",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "start:nodemon",
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
@ -13,7 +13,7 @@ import "semantic-ui-css/semantic.css";
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<Container>
|
||||
<Container className="app">
|
||||
{/* <MessageTest /> */}
|
||||
<DevicesView />
|
||||
<MessagesView />
|
||||
|
@ -1,25 +1,25 @@
|
||||
import * as classNames from "classnames";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { Header, Icon, Item } from "semantic-ui-react";
|
||||
import { Grid, Header, Icon, Item } from "semantic-ui-react";
|
||||
|
||||
import { injectState, MqttApiState } from "@app/state";
|
||||
import { SprinklersDevice } from "@common/sprinklers";
|
||||
import { ProgramTable, RunSectionForm, SectionRunnerView, SectionTable } from ".";
|
||||
|
||||
const ConnectionState = ({ connected }: { connected: boolean }) => {
|
||||
function ConnectionState({ connected, className }: { connected: boolean, className?: string }) {
|
||||
const classes = classNames({
|
||||
"device--connectionState": true,
|
||||
"device--connectionState-connected": connected,
|
||||
"device--connectionState-disconnected": !connected,
|
||||
});
|
||||
}, className);
|
||||
return (
|
||||
<span className={classes}>
|
||||
<div className={classes}>
|
||||
<Icon name={connected ? "linkify" : "unlinkify"} />
|
||||
{connected ? "Connected" : "Disconnected"}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
interface DeviceViewProps {
|
||||
deviceId: string;
|
||||
@ -44,14 +44,20 @@ class DeviceView extends React.Component<DeviceViewProps> {
|
||||
<Item.Image src={require("@app/images/raspberry_pi.png")} />
|
||||
<Item.Content>
|
||||
<Header as="h1">
|
||||
<span>Device </span><kbd>{id}</kbd>
|
||||
<div>Device <kbd>{id}</kbd></div>
|
||||
<ConnectionState connected={connected} />
|
||||
</Header>
|
||||
<Item.Meta>
|
||||
Raspberry Pi Grinklers Instance
|
||||
</Item.Meta>
|
||||
<SectionTable sections={sections} />
|
||||
<RunSectionForm sections={sections} />
|
||||
<Grid>
|
||||
<Grid.Column mobile={16} largeScreen={8}>
|
||||
<SectionTable sections={sections} />
|
||||
</Grid.Column>
|
||||
<Grid.Column mobile={16} largeScreen={8}>
|
||||
<RunSectionForm sections={sections} />
|
||||
</Grid.Column>
|
||||
</Grid>
|
||||
<ProgramTable programs={programs} />
|
||||
<SectionRunnerView sectionRunner={sectionRunner} sections={sections} />
|
||||
</Item.Content>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import * as classNames from "classnames";
|
||||
import * as React from "react";
|
||||
import { Input, InputProps } from "semantic-ui-react";
|
||||
|
||||
@ -5,13 +6,15 @@ import { Duration } from "@common/Duration";
|
||||
|
||||
export default class DurationInput extends React.Component<{
|
||||
duration: Duration,
|
||||
onDurationChange: (newDuration: Duration) => void;
|
||||
onDurationChange: (newDuration: Duration) => void,
|
||||
className?: string,
|
||||
}> {
|
||||
render() {
|
||||
const duration = this.props.duration;
|
||||
const className = classNames("field", "durationInput", this.props.className);
|
||||
// const editing = this.props.onDurationChange != null;
|
||||
return (
|
||||
<div className="field durationInput">
|
||||
<div className={className}>
|
||||
<label>Duration</label>
|
||||
<div className="fields">
|
||||
<Input
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { computed } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { DropdownItemProps, DropdownProps, Form, Header, Segment } from "semantic-ui-react";
|
||||
import { DropdownItemProps, DropdownProps, Form, Grid, Header, Segment } from "semantic-ui-react";
|
||||
|
||||
import { Duration } from "@common/Duration";
|
||||
import log from "@common/logger";
|
||||
@ -29,7 +29,7 @@ export default class RunSectionForm extends React.Component<{
|
||||
<Segment>
|
||||
<Header>Run Section</Header>
|
||||
<Form>
|
||||
<Form.Group>
|
||||
<Form.Group className="doubling stackable three column ui grid">
|
||||
<Form.Select
|
||||
label="Section"
|
||||
placeholder="Section"
|
||||
@ -37,7 +37,10 @@ export default class RunSectionForm extends React.Component<{
|
||||
value={section}
|
||||
onChange={this.onSectionChange}
|
||||
/>
|
||||
<DurationInput duration={duration} onDurationChange={this.onDurationChange} />
|
||||
<DurationInput
|
||||
duration={duration}
|
||||
onDurationChange={this.onDurationChange}
|
||||
/>
|
||||
{/*Label must be to align it properly*/}
|
||||
<Form.Button
|
||||
label=" "
|
||||
|
@ -34,7 +34,7 @@ export default class SectionTable extends React.Component<{ sections: Section[]
|
||||
render() {
|
||||
const rows = this.props.sections.map(SectionTable.renderRow);
|
||||
return (
|
||||
<Table celled striped>
|
||||
<Table celled striped unstackable>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell colSpan="3">Sections</Table.HeaderCell>
|
||||
|
11
app/state/StateBase.ts
Normal file
11
app/state/StateBase.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { ISprinklersApi } from "@common/sprinklers";
|
||||
import { UiMessage, UiStore } from "./ui";
|
||||
|
||||
export default abstract class StateBase {
|
||||
abstract readonly sprinklersApi: ISprinklersApi;
|
||||
uiStore = new UiStore();
|
||||
|
||||
start() {
|
||||
this.sprinklersApi.start();
|
||||
}
|
||||
}
|
@ -1,32 +1,3 @@
|
||||
import { ISprinklersApi } from "@common/sprinklers";
|
||||
import { MqttApiClient } from "@common/sprinklers/mqtt";
|
||||
import { WebApiClient } from "./websocket";
|
||||
|
||||
import { UiMessage, UiStore } from "./ui";
|
||||
export { UiMessage, UiStore };
|
||||
export { UiMessage, UiStore } from "./ui";
|
||||
export * from "./inject";
|
||||
|
||||
export abstract class StateBase {
|
||||
abstract readonly sprinklersApi: ISprinklersApi;
|
||||
uiStore = new UiStore();
|
||||
|
||||
constructor() {
|
||||
this.uiStore.addMessage({ header: "asdf", content: "boo!", error: true });
|
||||
}
|
||||
|
||||
start() {
|
||||
this.sprinklersApi.start();
|
||||
}
|
||||
}
|
||||
|
||||
const isDev = process.env.NODE_ENV === "development";
|
||||
|
||||
export class MqttApiState extends StateBase {
|
||||
sprinklersApi = new MqttApiClient(`ws://${location.hostname}:1884`);
|
||||
}
|
||||
|
||||
export class WebApiState extends StateBase {
|
||||
sprinklersApi = new WebApiClient(isDev ?
|
||||
`ws://${location.hostname}:8080` :
|
||||
`ws://${location.host}`);
|
||||
}
|
||||
export { default as StateBase } from "./StateBase";
|
||||
|
14
app/state/web.ts
Normal file
14
app/state/web.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { MqttApiClient } from "@common/sprinklers/mqtt";
|
||||
import StateBase from "./StateBase";
|
||||
import { WebApiClient } from "./websocket";
|
||||
|
||||
const isDev = process.env.NODE_ENV === "development";
|
||||
const websocketPort = isDev ? 8080 : location.port;
|
||||
|
||||
export class MqttApiState extends StateBase {
|
||||
sprinklersApi = new MqttApiClient(`ws://${location.hostname}:1884`);
|
||||
}
|
||||
|
||||
export class WebApiState extends StateBase {
|
||||
sprinklersApi = new WebApiClient(`ws://${location.hostname}:${websocketPort}`);
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
.app {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.device--connectionState {
|
||||
margin-left: .75em;
|
||||
font-size: .75em;
|
||||
|
@ -10,6 +10,10 @@ export abstract class SprinklersDevice {
|
||||
@observable programs: Program[] = [];
|
||||
@observable sectionRunner: SectionRunner;
|
||||
|
||||
sectionConstructor: typeof Section = Section;
|
||||
sectionRunnerConstructor: typeof SectionRunner = SectionRunner;
|
||||
programConstructor: typeof Program = Program;
|
||||
|
||||
constructor() {
|
||||
this.sectionRunner = new (this.sectionRunnerConstructor)(this);
|
||||
}
|
||||
@ -45,16 +49,6 @@ export abstract class SprinklersDevice {
|
||||
return this.makeRequest({ ...opts, type: "pauseSectionRunner" });
|
||||
}
|
||||
|
||||
get sectionConstructor(): typeof Section {
|
||||
return Section;
|
||||
}
|
||||
get sectionRunnerConstructor(): typeof SectionRunner {
|
||||
return SectionRunner;
|
||||
}
|
||||
get programConstructor(): typeof Program {
|
||||
return Program;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `SprinklersDevice{id="${this.id}", connected=${this.connected}, ` +
|
||||
`sections=[${this.sections}], ` +
|
||||
|
@ -132,6 +132,9 @@ class MqttSprinklersDevice extends s.SprinklersDevice {
|
||||
|
||||
constructor(apiClient: MqttApiClient, prefix: string) {
|
||||
super();
|
||||
this.sectionConstructor = MqttSection;
|
||||
this.sectionRunnerConstructor = MqttSectionRunner;
|
||||
this.programConstructor = MqttProgram;
|
||||
this.apiClient = apiClient;
|
||||
this.prefix = prefix;
|
||||
this.sectionRunner = new MqttSectionRunner(this);
|
||||
@ -141,10 +144,6 @@ class MqttSprinklersDevice extends s.SprinklersDevice {
|
||||
return this.prefix;
|
||||
}
|
||||
|
||||
get sectionConstructor() { return MqttSection; }
|
||||
get sectionRunnerConstructor() { return MqttSectionRunner; }
|
||||
get programConstructor() { return MqttProgram; }
|
||||
|
||||
doSubscribe() {
|
||||
const topics = subscriptions.map((filter) => this.prefix + filter);
|
||||
this.apiClient.client.subscribe(topics, { qos: 1 });
|
||||
|
Loading…
x
Reference in New Issue
Block a user