Improved components structure; better paho-mqtt definitions
This commit is contained in:
parent
e8705f0224
commit
10805f9fb1
@ -1,287 +0,0 @@
|
||||
import * as React from "react";
|
||||
import {SyntheticEvent} from "react";
|
||||
import {computed} from "mobx";
|
||||
import DevTools from "mobx-react-devtools";
|
||||
import {observer} from "mobx-react";
|
||||
import {SprinklersDevice, Section, Program, Duration, Schedule} from "./sprinklers";
|
||||
import {Item, Table, Header, Segment, Form, Input, Button, DropdownItemProps, DropdownProps, Message} from "semantic-ui-react";
|
||||
import FontAwesome = require("react-fontawesome");
|
||||
import * as classNames from "classnames";
|
||||
|
||||
import "semantic-ui-css/semantic.css";
|
||||
import "font-awesome/css/font-awesome.css";
|
||||
import "app/style/app.css";
|
||||
import {Message as UiMessage, UiStore} from "./ui";
|
||||
|
||||
/* tslint:disable:object-literal-sort-keys */
|
||||
|
||||
@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 (
|
||||
<Table.Row key={index}>
|
||||
<Table.Cell className="section--number">{"" + (index + 1)}</Table.Cell>
|
||||
<Table.Cell className="section--name">{name}</Table.Cell>
|
||||
<Table.Cell className={classNames({
|
||||
"section--state": true,
|
||||
"section--state-true": state,
|
||||
"section--state-false": !state,
|
||||
})}>{state ?
|
||||
(<span><FontAwesome name="tint"/> Irrigating</span>)
|
||||
: "Not irrigating"}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
);
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (<Table celled striped>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell colSpan="3">Sections</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell className="section--number">#</Table.HeaderCell>
|
||||
<Table.HeaderCell className="section--name">Name</Table.HeaderCell>
|
||||
<Table.HeaderCell className="section--state">State</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{
|
||||
this.props.sections.map(SectionTable.renderRow)
|
||||
}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DurationInput extends React.Component<{
|
||||
duration: Duration,
|
||||
onDurationChange?: (newDuration: Duration) => void;
|
||||
}, void> {
|
||||
public render() {
|
||||
const duration = this.props.duration;
|
||||
// const editing = this.props.onDurationChange != null;
|
||||
return <div className="field durationInput">
|
||||
<label>Duration</label>
|
||||
<div className="fields">
|
||||
<Input type="number" className="field durationInput--minutes"
|
||||
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"/>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
private onMinutesChange = (e, {value}) => {
|
||||
if (value.length === 0 || isNaN(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)) {
|
||||
return;
|
||||
}
|
||||
const newSeconds = parseInt(value, 10);
|
||||
this.props.onDurationChange(this.props.duration.withSeconds(newSeconds));
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class RunSectionForm extends React.Component<{
|
||||
sections: Section[],
|
||||
}, {
|
||||
duration: Duration,
|
||||
section: number | "",
|
||||
}> {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
duration: new Duration(1, 1),
|
||||
section: "",
|
||||
};
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {section, duration} = this.state;
|
||||
return <Segment>
|
||||
<Header>Run Section</Header>
|
||||
<Form>
|
||||
<Form.Group>
|
||||
<Form.Select label="Section" placeholder="Section" options={this.sectionOptions} value={section}
|
||||
onChange={this.onSectionChange}/>
|
||||
<DurationInput duration={duration} onDurationChange={this.onDurationChange}/>
|
||||
{/*Label must be to align it properly*/}
|
||||
<Form.Button label=" " primary onClick={this.run} disabled={!this.isValid}>Run</Form.Button>
|
||||
</Form.Group>
|
||||
</Form>
|
||||
</Segment>;
|
||||
}
|
||||
|
||||
private onSectionChange = (e: SyntheticEvent<HTMLElement>, v: DropdownProps) => {
|
||||
this.setState({section: v.value as number});
|
||||
}
|
||||
|
||||
private onDurationChange = (newDuration: Duration) => {
|
||||
this.setState({duration: newDuration});
|
||||
}
|
||||
|
||||
private run = (e: SyntheticEvent<HTMLElement>) => {
|
||||
e.preventDefault();
|
||||
const section: Section = this.props.sections[this.state.section];
|
||||
console.log(`should run 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));
|
||||
}
|
||||
|
||||
private get isValid(): boolean {
|
||||
return typeof this.state.section === "number";
|
||||
}
|
||||
|
||||
@computed
|
||||
private get sectionOptions(): DropdownItemProps[] {
|
||||
return this.props.sections.map((s, i) => ({
|
||||
text: s ? s.name : null,
|
||||
value: i,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class ScheduleView extends React.PureComponent<{ schedule: Schedule }, void> {
|
||||
public render() {
|
||||
return (
|
||||
<div>{JSON.stringify(this.props.schedule)}</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class ProgramTable extends React.PureComponent<{ programs: Program[] }, void> {
|
||||
private static renderRow(program: Program, i: number): JSX.Element[] {
|
||||
if (!program) {
|
||||
return null;
|
||||
}
|
||||
const {name, running, enabled, schedule, sequence} = program;
|
||||
return [
|
||||
<Table.Row key={i}>
|
||||
<Table.Cell className="program--number">{"" + (i + 1)}</Table.Cell>
|
||||
<Table.Cell className="program--name">{name}</Table.Cell>
|
||||
<Table.Cell className="program--running">{running ? "Running" : "Not running"}</Table.Cell>
|
||||
<Table.Cell className="program--enabled">{enabled ? "Enabled" : "Not enabled"}</Table.Cell>
|
||||
</Table.Row>
|
||||
,
|
||||
<Table.Row key={i + .5}>
|
||||
<Table.Cell className="program--sequence" colSpan="4">
|
||||
<ul>
|
||||
{sequence.map((item) =>
|
||||
(<li>Section {item.section + 1 + ""} for
|
||||
{item.duration.minutes}M {item.duration.seconds}S</li>))}
|
||||
</ul>
|
||||
<ScheduleView schedule={schedule}/>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
,
|
||||
];
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<Table celled>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell colSpan="7">Programs</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell className="program--number">#</Table.HeaderCell>
|
||||
<Table.HeaderCell className="program--name">Name</Table.HeaderCell>
|
||||
<Table.HeaderCell className="program--running">Running?</Table.HeaderCell>
|
||||
<Table.HeaderCell className="program--enabled">Enabled?</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{
|
||||
Array.prototype.concat.apply([], this.props.programs.map(ProgramTable.renderRow))
|
||||
}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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"}/>
|
||||
|
||||
{connected ? "Connected" : "Disconnected"}
|
||||
</span>;
|
||||
|
||||
@observer
|
||||
class DeviceView extends React.PureComponent<{ device: SprinklersDevice }, void> {
|
||||
public render() {
|
||||
const {id, connected, sections, programs} = this.props.device;
|
||||
return (
|
||||
<Item>
|
||||
<Item.Image src={require<string>("app/images/raspberry_pi.png")}/>
|
||||
<Item.Content>
|
||||
<Header as="h1">
|
||||
<span>Device </span><kbd>{id}</kbd>
|
||||
<ConnectionState connected={connected}/>
|
||||
</Header>
|
||||
<Item.Meta>
|
||||
|
||||
</Item.Meta>
|
||||
<SectionTable sections={sections}/>
|
||||
<RunSectionForm sections={sections}/>
|
||||
<ProgramTable programs={programs}/>
|
||||
</Item.Content>
|
||||
</Item>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class MessagesView extends React.PureComponent<{uiStore: UiStore}, void> {
|
||||
public render() {
|
||||
return <div>
|
||||
{this.props.uiStore.messages.map(this.renderMessage)}
|
||||
</div>;
|
||||
}
|
||||
|
||||
private renderMessage = (message: UiMessage, index: number) => {
|
||||
const {header, content, type} = message;
|
||||
return <Message header={header} content={content} success={type === UiMessage.Type.Success}
|
||||
info={type === UiMessage.Type.Info} warning={type === UiMessage.Type.Warning}
|
||||
error={type === UiMessage.Type.Error} onDismiss={() => this.dismiss(index)}/>;
|
||||
}
|
||||
|
||||
private dismiss(index: number) {
|
||||
this.props.uiStore.messages.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
export default class App extends React.PureComponent<{ device: SprinklersDevice, uiStore: UiStore }, any> {
|
||||
public render() {
|
||||
return <Item.Group divided>
|
||||
<MessagesView uiStore={this.props.uiStore} />
|
||||
<DeviceView device={this.props.device}/>
|
||||
<DevTools />
|
||||
</Item.Group>;
|
||||
}
|
||||
}
|
22
app/script/components/App.tsx
Normal file
22
app/script/components/App.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import * as React from "react";
|
||||
import DevTools from "mobx-react-devtools";
|
||||
import {observer} from "mobx-react";
|
||||
import {SprinklersDevice} from "../sprinklers";
|
||||
import {Item} from "semantic-ui-react";
|
||||
import {UiStore} from "../ui";
|
||||
import {MessagesView, DeviceView} from ".";
|
||||
|
||||
import "semantic-ui-css/semantic.css";
|
||||
import "font-awesome/css/font-awesome.css";
|
||||
import "app/style/app.css";
|
||||
|
||||
@observer
|
||||
export default class App extends React.PureComponent<{ device: SprinklersDevice, uiStore: UiStore }, any> {
|
||||
render() {
|
||||
return <Item.Group divided>
|
||||
<MessagesView uiStore={this.props.uiStore}/>
|
||||
<DeviceView device={this.props.device}/>
|
||||
<DevTools />
|
||||
</Item.Group>;
|
||||
}
|
||||
}
|
43
app/script/components/DeviceView.tsx
Normal file
43
app/script/components/DeviceView.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import * as React from "react";
|
||||
import {observer} from "mobx-react";
|
||||
import {Item, Header} from "semantic-ui-react";
|
||||
import FontAwesome = require("react-fontawesome");
|
||||
import * as classNames from "classnames";
|
||||
|
||||
import {SprinklersDevice} from "../sprinklers";
|
||||
import {SectionTable, RunSectionForm, ProgramTable} from ".";
|
||||
|
||||
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"}/>
|
||||
|
||||
{connected ? "Connected" : "Disconnected"}
|
||||
</span>;
|
||||
|
||||
@observer
|
||||
export default class DeviceView extends React.PureComponent<{ device: SprinklersDevice }, void> {
|
||||
render() {
|
||||
const {id, connected, sections, programs} = this.props.device;
|
||||
return (
|
||||
<Item>
|
||||
<Item.Image src={require<string>("app/images/raspberry_pi.png")}/>
|
||||
<Item.Content>
|
||||
<Header as="h1">
|
||||
<span>Device </span><kbd>{id}</kbd>
|
||||
<ConnectionState connected={connected}/>
|
||||
</Header>
|
||||
<Item.Meta>
|
||||
|
||||
</Item.Meta>
|
||||
<SectionTable sections={sections}/>
|
||||
<RunSectionForm sections={sections}/>
|
||||
<ProgramTable programs={programs}/>
|
||||
</Item.Content>
|
||||
</Item>
|
||||
);
|
||||
}
|
||||
}
|
40
app/script/components/DurationInput.tsx
Normal file
40
app/script/components/DurationInput.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import * as React from "react";
|
||||
import {Duration} from "../sprinklers";
|
||||
import {Input} from "semantic-ui-react";
|
||||
|
||||
export default class DurationInput extends React.Component<{
|
||||
duration: Duration,
|
||||
onDurationChange?: (newDuration: Duration) => void;
|
||||
}, void> {
|
||||
render() {
|
||||
const duration = this.props.duration;
|
||||
// const editing = this.props.onDurationChange != null;
|
||||
return <div className="field durationInput">
|
||||
<label>Duration</label>
|
||||
<div className="fields">
|
||||
<Input type="number" className="field durationInput--minutes"
|
||||
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"/>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
private onMinutesChange = (e, {value}) => {
|
||||
if (value.length === 0 || isNaN(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)) {
|
||||
return;
|
||||
}
|
||||
const newSeconds = parseInt(value, 10);
|
||||
this.props.onDurationChange(this.props.duration.withSeconds(newSeconds));
|
||||
}
|
||||
}
|
24
app/script/components/MessagesView.tsx
Normal file
24
app/script/components/MessagesView.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import * as React from "react";
|
||||
import {observer} from "mobx-react";
|
||||
import {UiStore, Message as UiMessage} from "../ui";
|
||||
import {Message} from "semantic-ui-react";
|
||||
|
||||
@observer
|
||||
export default class MessagesView extends React.PureComponent<{ uiStore: UiStore }, void> {
|
||||
render() {
|
||||
return <div>
|
||||
{this.props.uiStore.messages.map(this.renderMessage)}
|
||||
</div>;
|
||||
}
|
||||
|
||||
private renderMessage = (message: UiMessage, index: number) => {
|
||||
const {header, content, type} = message;
|
||||
return <Message header={header} content={content} success={type === UiMessage.Type.Success}
|
||||
info={type === UiMessage.Type.Info} warning={type === UiMessage.Type.Warning}
|
||||
error={type === UiMessage.Type.Error} onDismiss={() => this.dismiss(index)}/>;
|
||||
}
|
||||
|
||||
private dismiss(index: number) {
|
||||
this.props.uiStore.messages.splice(index, 1);
|
||||
}
|
||||
}
|
66
app/script/components/ProgramTable.tsx
Normal file
66
app/script/components/ProgramTable.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import * as React from "react";
|
||||
import {observer} from "mobx-react";
|
||||
import {Program, Schedule} from "../sprinklers";
|
||||
import {Table} from "semantic-ui-react";
|
||||
|
||||
@observer
|
||||
export class ScheduleView extends React.PureComponent<{ schedule: Schedule }, void> {
|
||||
render() {
|
||||
return (
|
||||
<div>{JSON.stringify(this.props.schedule)}</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
export default class ProgramTable extends React.PureComponent<{ programs: Program[] }, void> {
|
||||
private static renderRow(program: Program, i: number): JSX.Element[] {
|
||||
if (!program) {
|
||||
return null;
|
||||
}
|
||||
const {name, running, enabled, schedule, sequence} = program;
|
||||
return [
|
||||
<Table.Row key={i}>
|
||||
<Table.Cell className="program--number">{"" + (i + 1)}</Table.Cell>
|
||||
<Table.Cell className="program--name">{name}</Table.Cell>
|
||||
<Table.Cell className="program--running">{running ? "Running" : "Not running"}</Table.Cell>
|
||||
<Table.Cell className="program--enabled">{enabled ? "Enabled" : "Not enabled"}</Table.Cell>
|
||||
</Table.Row>
|
||||
,
|
||||
<Table.Row key={i + .5}>
|
||||
<Table.Cell className="program--sequence" colSpan="4">
|
||||
<ul>
|
||||
{sequence.map((item) =>
|
||||
(<li>Section {item.section + 1 + ""} for
|
||||
{item.duration.minutes}M {item.duration.seconds}S</li>))}
|
||||
</ul>
|
||||
<ScheduleView schedule={schedule}/>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
,
|
||||
];
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Table celled>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell colSpan="7">Programs</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell className="program--number">#</Table.HeaderCell>
|
||||
<Table.HeaderCell className="program--name">Name</Table.HeaderCell>
|
||||
<Table.HeaderCell className="program--running">Running?</Table.HeaderCell>
|
||||
<Table.HeaderCell className="program--enabled">Enabled?</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{
|
||||
Array.prototype.concat.apply([], this.props.programs.map(ProgramTable.renderRow))
|
||||
}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
}
|
68
app/script/components/RunSectionForm.tsx
Normal file
68
app/script/components/RunSectionForm.tsx
Normal file
@ -0,0 +1,68 @@
|
||||
import * as React from "react";
|
||||
import {SyntheticEvent} from "react";
|
||||
import {computed} from "mobx";
|
||||
import {observer} from "mobx-react";
|
||||
import {Duration, Section} from "../sprinklers";
|
||||
import {Segment, Header, Form, DropdownProps, DropdownItemProps} from "semantic-ui-react";
|
||||
import {DurationInput} from ".";
|
||||
|
||||
@observer
|
||||
export default class RunSectionForm extends React.Component<{
|
||||
sections: Section[],
|
||||
}, {
|
||||
duration: Duration,
|
||||
section: number | "",
|
||||
}> {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
duration: new Duration(1, 1),
|
||||
section: "",
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const {section, duration} = this.state;
|
||||
return <Segment>
|
||||
<Header>Run Section</Header>
|
||||
<Form>
|
||||
<Form.Group>
|
||||
<Form.Select label="Section" placeholder="Section" options={this.sectionOptions} value={section}
|
||||
onChange={this.onSectionChange}/>
|
||||
<DurationInput duration={duration} onDurationChange={this.onDurationChange}/>
|
||||
{/*Label must be to align it properly*/}
|
||||
<Form.Button label=" " primary onClick={this.run} disabled={!this.isValid}>Run</Form.Button>
|
||||
</Form.Group>
|
||||
</Form>
|
||||
</Segment>;
|
||||
}
|
||||
|
||||
private onSectionChange = (e: SyntheticEvent<HTMLElement>, v: DropdownProps) => {
|
||||
this.setState({section: v.value as number});
|
||||
}
|
||||
|
||||
private onDurationChange = (newDuration: Duration) => {
|
||||
this.setState({duration: newDuration});
|
||||
}
|
||||
|
||||
private run = (e: SyntheticEvent<HTMLElement>) => {
|
||||
e.preventDefault();
|
||||
const section: Section = this.props.sections[this.state.section];
|
||||
console.log(`should run 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));
|
||||
}
|
||||
|
||||
private get isValid(): boolean {
|
||||
return typeof this.state.section === "number";
|
||||
}
|
||||
|
||||
@computed
|
||||
private get sectionOptions(): DropdownItemProps[] {
|
||||
return this.props.sections.map((s, i) => ({
|
||||
text: s ? s.name : null,
|
||||
value: i,
|
||||
}));
|
||||
}
|
||||
}
|
54
app/script/components/SectionTable.tsx
Normal file
54
app/script/components/SectionTable.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import * as React from "react";
|
||||
import {observer} from "mobx-react";
|
||||
import * as classNames from "classnames";
|
||||
import {Table} from "semantic-ui-react";
|
||||
import FontAwesome = require("react-fontawesome");
|
||||
|
||||
import {Section} from "../sprinklers";
|
||||
|
||||
/* tslint:disable:object-literal-sort-keys */
|
||||
|
||||
@observer
|
||||
export default class SectionTable extends React.PureComponent<{ sections: Section[] }, void> {
|
||||
private static renderRow(section: Section, index: number) {
|
||||
if (!section) {
|
||||
return null;
|
||||
}
|
||||
const {name, state} = section;
|
||||
return (
|
||||
<Table.Row key={index}>
|
||||
<Table.Cell className="section--number">{"" + (index + 1)}</Table.Cell>
|
||||
<Table.Cell className="section--name">{name}</Table.Cell>
|
||||
<Table.Cell className={classNames({
|
||||
"section--state": true,
|
||||
"section--state-true": state,
|
||||
"section--state-false": !state,
|
||||
})}>{state ?
|
||||
(<span><FontAwesome name="tint"/> Irrigating</span>)
|
||||
: "Not irrigating"}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (<Table celled striped>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell colSpan="3">Sections</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell className="section--number">#</Table.HeaderCell>
|
||||
<Table.HeaderCell className="section--name">Name</Table.HeaderCell>
|
||||
<Table.HeaderCell className="section--state">State</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{
|
||||
this.props.sections.map(SectionTable.renderRow)
|
||||
}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
}
|
7
app/script/components/index.ts
Normal file
7
app/script/components/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export {default as App} from "./App";
|
||||
export {default as DeviceView} from "./DeviceView";
|
||||
export {default as DurationInput} from "./DurationInput";
|
||||
export {default as MessagesView} from "./MessagesView";
|
||||
export {default as ProgramTable} from "./ProgramTable";
|
||||
export {default as RunSectionForm} from "./RunSectionForm";
|
||||
export {default as SectionTable} from "./SectionTable";
|
@ -2,7 +2,7 @@ import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import { AppContainer } from "react-hot-loader";
|
||||
|
||||
import App from "./App";
|
||||
import App from "./components/App";
|
||||
import { MqttApiClient } from "./mqtt";
|
||||
import {Message, UiStore} from "./ui";
|
||||
|
||||
@ -19,8 +19,8 @@ ReactDOM.render(<AppContainer>
|
||||
</AppContainer>, rootElem);
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept("./App", () => {
|
||||
const NextApp = require<any>("./App").default;
|
||||
module.hot.accept("./components/App", () => {
|
||||
const NextApp = require<any>("./components/App").default;
|
||||
ReactDOM.render(<AppContainer>
|
||||
<App device={device} uiStore={uiStore} />
|
||||
</AppContainer>, rootElem);
|
||||
|
@ -1,9 +1,9 @@
|
||||
import "paho-mqtt/mqttws31";
|
||||
import "paho-mqtt";
|
||||
import MQTT = Paho.MQTT;
|
||||
|
||||
import {EventEmitter} from "events";
|
||||
import {
|
||||
SprinklersDevice, ISprinklersApi, Section, Program, IProgramItem, Schedule, ITimeOfDay, Weekday, Duration,
|
||||
SprinklersDevice, ISprinklersApi, Section, Program, Schedule, ITimeOfDay, Duration,
|
||||
} from "./sprinklers";
|
||||
import {checkedIndexOf} from "./utils";
|
||||
import * as Promise from "bluebird";
|
||||
@ -13,11 +13,11 @@ export class MqttApiClient extends EventEmitter implements ISprinklersApi {
|
||||
return "sprinklers3-MqttApiClient-" + Math.round(Math.random() * 1000);
|
||||
}
|
||||
|
||||
public client: MQTT.Client;
|
||||
client: MQTT.Client;
|
||||
|
||||
public connected: boolean;
|
||||
connected: boolean;
|
||||
|
||||
public devices: { [prefix: string]: MqttSprinklersDevice } = {};
|
||||
devices: { [prefix: string]: MqttSprinklersDevice } = {};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@ -27,7 +27,7 @@ export class MqttApiClient extends EventEmitter implements ISprinklersApi {
|
||||
// (this.client as any).trace = (m => console.log(m));
|
||||
}
|
||||
|
||||
public start() {
|
||||
start() {
|
||||
console.log("connecting to mqtt with client id %s", this.client.clientId);
|
||||
this.client.connect({
|
||||
onFailure: (e) => {
|
||||
@ -44,7 +44,7 @@ export class MqttApiClient extends EventEmitter implements ISprinklersApi {
|
||||
});
|
||||
}
|
||||
|
||||
public getDevice(prefix: string): SprinklersDevice {
|
||||
getDevice(prefix: string): SprinklersDevice {
|
||||
if (/\//.test(prefix)) {
|
||||
throw new Error("Prefix cannot contain a /");
|
||||
}
|
||||
@ -57,7 +57,7 @@ export class MqttApiClient extends EventEmitter implements ISprinklersApi {
|
||||
return this.devices[prefix];
|
||||
}
|
||||
|
||||
public removeDevice(prefix: string) {
|
||||
removeDevice(prefix: string) {
|
||||
const device = this.devices[prefix];
|
||||
if (!device) {
|
||||
return;
|
||||
@ -93,8 +93,8 @@ export class MqttApiClient extends EventEmitter implements ISprinklersApi {
|
||||
}
|
||||
|
||||
class MqttSprinklersDevice extends SprinklersDevice {
|
||||
public readonly apiClient: MqttApiClient;
|
||||
public readonly prefix: string;
|
||||
readonly apiClient: MqttApiClient;
|
||||
readonly prefix: string;
|
||||
|
||||
private responseCallbacks: {
|
||||
[rid: number]: ResponseCallback;
|
||||
@ -106,13 +106,13 @@ class MqttSprinklersDevice extends SprinklersDevice {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public doSubscribe() {
|
||||
doSubscribe() {
|
||||
const c = this.apiClient.client;
|
||||
this.subscriptions
|
||||
.forEach((filter) => c.subscribe(filter, {qos: 1}));
|
||||
}
|
||||
|
||||
public doUnsubscribe() {
|
||||
doUnsubscribe() {
|
||||
const c = this.apiClient.client;
|
||||
this.subscriptions
|
||||
.forEach((filter) => c.unsubscribe(filter));
|
||||
@ -123,7 +123,7 @@ class MqttSprinklersDevice extends SprinklersDevice {
|
||||
* @param topic The topic, with prefix removed
|
||||
* @param payload The payload string
|
||||
*/
|
||||
public onMessage(topic: string, payload: string) {
|
||||
onMessage(topic: string, payload: string) {
|
||||
if (topic === "connected") {
|
||||
this.connected = (payload === "true");
|
||||
// console.log(`MqttSprinklersDevice with prefix ${this.prefix}: ${this.connected}`)
|
||||
@ -131,6 +131,7 @@ class MqttSprinklersDevice extends SprinklersDevice {
|
||||
}
|
||||
let matches = topic.match(/^sections(?:\/(\d+)(?:\/?(.+))?)?$/);
|
||||
if (matches != null) {
|
||||
//noinspection JSUnusedLocalSymbols
|
||||
const [_topic, secStr, subTopic] = matches;
|
||||
// console.log(`section: ${secStr}, topic: ${subTopic}, payload: ${payload}`);
|
||||
if (!secStr) { // new number of sections
|
||||
@ -147,6 +148,7 @@ class MqttSprinklersDevice extends SprinklersDevice {
|
||||
}
|
||||
matches = topic.match(/^programs(?:\/(\d+)(?:\/?(.+))?)?$/);
|
||||
if (matches != null) {
|
||||
//noinspection JSUnusedLocalSymbols
|
||||
const [_topic, progStr, subTopic] = matches;
|
||||
// console.log(`program: ${progStr}, topic: ${subTopic}, payload: ${payload}`);
|
||||
if (!progStr) { // new number of programs
|
||||
@ -163,6 +165,7 @@ class MqttSprinklersDevice extends SprinklersDevice {
|
||||
}
|
||||
matches = topic.match(/^responses\/(\d+)$/);
|
||||
if (matches != null) {
|
||||
//noinspection JSUnusedLocalSymbols
|
||||
const [_topic, respIdStr] = matches;
|
||||
console.log(`response: ${respIdStr}`);
|
||||
const respId = parseInt(respIdStr, 10);
|
||||
@ -180,7 +183,7 @@ class MqttSprinklersDevice extends SprinklersDevice {
|
||||
return this.prefix;
|
||||
}
|
||||
|
||||
public runSection(section: Section | number, duration: Duration) {
|
||||
runSection(section: Section | number, duration: Duration) {
|
||||
const sectionNum = checkedIndexOf(section, this.sections, "Section");
|
||||
return this.makeRequest(`sections/${sectionNum}/run`,
|
||||
{
|
||||
@ -188,23 +191,20 @@ class MqttSprinklersDevice extends SprinklersDevice {
|
||||
} as IRunSectionJSON);
|
||||
}
|
||||
|
||||
public runProgram(program: Program | number) {
|
||||
runProgram(program: Program | number) {
|
||||
const programNum = checkedIndexOf(program, this.programs, "Program");
|
||||
return this.makeRequest(`programs/${programNum}/run`, {});
|
||||
}
|
||||
|
||||
//noinspection JSMethodCanBeStatic
|
||||
private nextRequestId(): number {
|
||||
return Math.floor(Math.random() * 1000000000);
|
||||
}
|
||||
|
||||
private makeRequest(topic: string, payload: object | string): Promise<IResponseData> {
|
||||
return new Promise<IResponseData>((resolve, reject) => {
|
||||
let payloadStr: string;
|
||||
if (typeof payload === "string") {
|
||||
payloadStr = payload;
|
||||
} else {
|
||||
payloadStr = JSON.stringify(payload);
|
||||
}
|
||||
const payloadStr = (typeof payload === "string") ?
|
||||
payload : JSON.stringify(payload);
|
||||
const message = new MQTT.Message(payloadStr);
|
||||
message.destinationName = this.prefix + "/" + topic;
|
||||
const requestId = this.nextRequestId();
|
||||
@ -250,7 +250,7 @@ interface IRunSectionJSON {
|
||||
}
|
||||
|
||||
class MqttSection extends Section {
|
||||
public onMessage(topic: string, payload: string) {
|
||||
onMessage(topic: string, payload: string) {
|
||||
if (topic === "state") {
|
||||
this.state = (payload === "true");
|
||||
} else if (topic == null) {
|
||||
@ -289,7 +289,7 @@ interface IProgramJSON {
|
||||
}
|
||||
|
||||
class MqttProgram extends Program {
|
||||
public onMessage(topic: string, payload: string) {
|
||||
onMessage(topic: string, payload: string) {
|
||||
if (topic === "running") {
|
||||
this.running = (payload === "true");
|
||||
} else if (topic == null) {
|
||||
@ -298,7 +298,7 @@ class MqttProgram extends Program {
|
||||
}
|
||||
}
|
||||
|
||||
public updateFromJSON(json: Partial<IProgramJSON>) {
|
||||
updateFromJSON(json: Partial<IProgramJSON>) {
|
||||
if (json.name != null) {
|
||||
this.name = json.name;
|
||||
}
|
||||
|
444
app/script/paho-mqtt.d.ts
vendored
444
app/script/paho-mqtt.d.ts
vendored
@ -1,76 +1,444 @@
|
||||
/* tslint:disable:interface-name */
|
||||
// Type definitions for paho-mqtt 1.0
|
||||
// Project: https://github.com/eclipse/paho.mqtt.javascript#readme
|
||||
// Definitions by: Alex Mikhalev <https://github.com/amikhalev>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
|
||||
declare namespace Paho {
|
||||
/**
|
||||
* Send and receive messages using web browsers.
|
||||
* <p>
|
||||
* This programming interface lets a JavaScript client application use the MQTT V3.1 or
|
||||
* V3.1.1 protocol to connect to an MQTT-supporting messaging server.
|
||||
*
|
||||
* The function supported includes:
|
||||
* <ol>
|
||||
* <li>Connecting to and disconnecting from a server. The server is identified by its host name and port number.
|
||||
* <li>Specifying options that relate to the communications link with the server,
|
||||
* for example the frequency of keep-alive heartbeats, and whether SSL/TLS is required.
|
||||
* <li>Subscribing to and receiving messages from MQTT Topics.
|
||||
* <li>Publishing messages to MQTT Topics.
|
||||
* </ol>
|
||||
* <p>
|
||||
* The API consists of two main objects:
|
||||
* <dl>
|
||||
* <dt><b>{@link Paho.MQTT.Client}</b></dt>
|
||||
* <dd>This contains methods that provide the functionality of the API,
|
||||
* including provision of callbacks that notify the application when a message
|
||||
* arrives from or is delivered to the messaging server,
|
||||
* or when the status of its connection to the messaging server changes.</dd>
|
||||
* <dt><b>{@link Paho.MQTT.Message}</b></dt>
|
||||
* <dd>This encapsulates the payload of the message along with various attributes
|
||||
* associated with its delivery, in particular the destination to which it has
|
||||
* been (or is about to be) sent.</dd>
|
||||
* </dl>
|
||||
* <p>
|
||||
* The programming interface validates parameters passed to it, and will throw
|
||||
* an Error containing an error message intended for developer use, if it detects
|
||||
* an error with any parameter.
|
||||
* <p>
|
||||
*
|
||||
* @namespace Paho.MQTT
|
||||
*/
|
||||
namespace MQTT {
|
||||
interface MQTTError { errorCode: string; errorMessage: string; }
|
||||
interface WithInvocationContext { invocationContext: object; }
|
||||
interface ErrorWithInvocationContext extends MQTTError, WithInvocationContext {}
|
||||
interface OnSubscribeSuccessParams extends WithInvocationContext { grantedQos: number; }
|
||||
/**
|
||||
* The Quality of Service used to deliver a message.
|
||||
* <dl>
|
||||
* <dt>0 Best effort (default).</dt>
|
||||
* <dt>1 At least once.</dt>
|
||||
* <dt>2 Exactly once.</dt>
|
||||
* </dl>
|
||||
*/
|
||||
type Qos = 0 | 1 | 2;
|
||||
|
||||
interface MQTTError {
|
||||
/** A number indicating the nature of the error. */
|
||||
errorCode: number;
|
||||
|
||||
/** Text describing the error */
|
||||
errorMessage: string;
|
||||
}
|
||||
interface WithInvocationContext {
|
||||
/**
|
||||
* <code>invocationContext</code> as passed in with the corresponding field in the connectOptions or
|
||||
* subscribeOptions.
|
||||
*/
|
||||
invocationContext: any;
|
||||
}
|
||||
interface ErrorWithInvocationContext extends MQTTError, WithInvocationContext {
|
||||
}
|
||||
interface OnSubscribeSuccessParams extends WithInvocationContext {
|
||||
grantedQos: Qos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the connect acknowledgement has been received from the server.
|
||||
* @param o
|
||||
* A single response object parameter is passed to the onSuccess callback containing the following fields:
|
||||
* <li><code>invocationContext</code> as passed in with the corresponding field in the connectOptions.
|
||||
*/
|
||||
type OnSuccessCallback = (o: WithInvocationContext) => void;
|
||||
|
||||
/**
|
||||
* Called when the subscribe acknowledgement has been received from the server.
|
||||
* @param o
|
||||
* A single response object parameter is passed to the onSuccess callback containing the following fields:
|
||||
* <li><code>invocationContext</code> as passed in with the corresponding field in the connectOptions.
|
||||
*/
|
||||
type OnSubscribeSuccessCallback = (o: OnSubscribeSuccessParams) => void;
|
||||
|
||||
/**
|
||||
* Called when the connect request has failed or timed out.
|
||||
* @param e
|
||||
* A single response object parameter is passed to the onFailure callback containing the following fields:
|
||||
* <li><code>invocationContext</code> as passed in with the corresponding field in the connectOptions.
|
||||
* <li><code>errorCode</code> a number indicating the nature of the error.
|
||||
* <li><code>errorMessage</code> text describing the error.
|
||||
*/
|
||||
type OnFailureCallback = (e: ErrorWithInvocationContext) => void;
|
||||
|
||||
/**
|
||||
* Called when a connection has been lost.
|
||||
* @param error A single response object parameter is passed to the onConnectionLost callback containing the
|
||||
* following fields:
|
||||
* <li>errorCode
|
||||
* <li>errorMessage
|
||||
*/
|
||||
type OnConnectionLostHandler = (error: MQTTError) => void;
|
||||
|
||||
/**
|
||||
* Called when a message was delivered or has arrived.
|
||||
* @param message The {@link Paho.MQTT.Message} that was delivered or has arrived.
|
||||
*/
|
||||
type OnMessageHandler = (message: Message) => void;
|
||||
|
||||
/**
|
||||
* Attributes used with a connection.
|
||||
*/
|
||||
interface ConnectionOptions {
|
||||
/**
|
||||
* If the connect has not succeeded within this number of seconds, it is deemed to have failed.
|
||||
* @default The default is 30 seconds.
|
||||
*/
|
||||
timeout?: number;
|
||||
/** Authentication username for this connection. */
|
||||
userName?: string;
|
||||
/** Authentication password for this connection. */
|
||||
password?: string;
|
||||
/** Sent by the server when the client disconnects abnormally. */
|
||||
willMessage?: Message;
|
||||
/**
|
||||
* The server disconnects this client if there is no activity for this number of seconds.
|
||||
* @default The default value of 60 seconds is assumed if not set.
|
||||
*/
|
||||
keepAliveInterval?: number;
|
||||
/**
|
||||
* If true(default) the client and server persistent state is deleted on successful connect.
|
||||
* @default true
|
||||
*/
|
||||
cleanSession?: boolean;
|
||||
/** If present and true, use an SSL Websocket connection. */
|
||||
useSSL?: boolean;
|
||||
invocationContext?: object;
|
||||
onSuccess?: (o: WithInvocationContext) => void;
|
||||
mqttVersion?: number;
|
||||
onFailure?: (e: ErrorWithInvocationContext) => void;
|
||||
/** Passed to the onSuccess callback or onFailure callback. */
|
||||
invocationContext?: any;
|
||||
/**
|
||||
* Called when the connect acknowledgement has been received from the server.
|
||||
*/
|
||||
onSuccess?: OnSuccessCallback;
|
||||
/**
|
||||
* Specifies the mqtt version to use when connecting
|
||||
* <dl>
|
||||
* <dt>3 - MQTT 3.1</dt>
|
||||
* <dt>4 - MQTT 3.1.1 (default)</dt>
|
||||
* </dl>
|
||||
* @default 4
|
||||
*/
|
||||
mqttVersion?: 3 | 4;
|
||||
/**
|
||||
* Called when the connect request has failed or timed out.
|
||||
*/
|
||||
onFailure?: OnFailureCallback;
|
||||
/**
|
||||
* If present this contains either a set of hostnames or fully qualified
|
||||
* WebSocket URIs (ws://example.com:1883/mqtt), that are tried in order in place of the host and port
|
||||
* paramater on the construtor. The hosts are tried one at at time in order until one of then succeeds.
|
||||
*/
|
||||
hosts?: string[];
|
||||
/**
|
||||
* If present the set of ports matching the hosts. If hosts contains URIs, this property is not used.
|
||||
*/
|
||||
ports?: number[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to control a subscription
|
||||
*/
|
||||
interface SubscribeOptions {
|
||||
qos?: number;
|
||||
invocationContext?: object;
|
||||
onSuccess?: (o: OnSubscribeSuccessParams) => void;
|
||||
onFailure?: (e: ErrorWithInvocationContext) => void;
|
||||
/** the maximum qos of any publications sent as a result of making this subscription. */
|
||||
qos?: Qos;
|
||||
/** passed to the onSuccess callback or onFailure callback. */
|
||||
invocationContext?: any;
|
||||
/** called when the subscribe acknowledgement has been received from the server. */
|
||||
onSuccess?: OnSubscribeSuccessCallback;
|
||||
/** called when the subscribe request has failed or timed out. */
|
||||
onFailure?: OnFailureCallback;
|
||||
/**
|
||||
* timeout which, if present, determines the number of seconds after which the onFailure calback is called.
|
||||
* The presence of a timeout does not prevent the onSuccess callback from being called when the subscribe
|
||||
* completes.
|
||||
*/
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
interface UnsubscribeOptions {
|
||||
invocationContext?: object;
|
||||
onSuccess?: (o: WithInvocationContext) => void;
|
||||
onFailure?: (e: ErrorWithInvocationContext) => void;
|
||||
/** passed to the onSuccess callback or onFailure callback. */
|
||||
invocationContext?: any;
|
||||
/** called when the unsubscribe acknowledgement has been received from the server. */
|
||||
onSuccess?: OnSuccessCallback;
|
||||
/** called when the unsubscribe request has failed or timed out. */
|
||||
onFailure?: OnFailureCallback;
|
||||
/**
|
||||
* timeout which, if present, determines the number of seconds after which the onFailure calback is called.
|
||||
* The presence of a timeout does not prevent the onSuccess callback from being called when the unsubscribe
|
||||
* completes.
|
||||
*/
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
interface TraceElement {
|
||||
severity: "Debug";
|
||||
message: string;
|
||||
}
|
||||
|
||||
type TraceFunction = (element: TraceElement) => void;
|
||||
|
||||
/**
|
||||
* The JavaScript application communicates to the server using a {@link Paho.MQTT.Client} object.
|
||||
*
|
||||
* Most applications will create just one Client object and then call its connect() method,
|
||||
* however applications can create more than one Client object if they wish.
|
||||
* In this case the combination of host, port and clientId attributes must be different for each Client object.
|
||||
*
|
||||
* The send, subscribe and unsubscribe methods are implemented as asynchronous JavaScript methods
|
||||
* (even though the underlying protocol exchange might be synchronous in nature).
|
||||
* This means they signal their completion by calling back to the application,
|
||||
* via Success or Failure callback functions provided by the application on the method in question.
|
||||
* Such callbacks are called at most once per method invocation and do not persist beyond the lifetime
|
||||
* of the script that made the invocation.
|
||||
*
|
||||
* In contrast there are some callback functions, most notably {@link onMessageArrived},
|
||||
* that are defined on the {@link Paho.MQTT.Client} object.
|
||||
* These may get called multiple times, and aren't directly related to specific method invocations made by the
|
||||
* client.
|
||||
*
|
||||
*/
|
||||
class Client {
|
||||
public readonly clientId: string;
|
||||
public readonly host: string;
|
||||
public readonly path: string;
|
||||
public readonly port: number;
|
||||
/** <i>read only</i> used when connecting to the server. */
|
||||
readonly clientId: string;
|
||||
|
||||
public onConnectionLost: OnConnectionLostHandler;
|
||||
public onMessageArrived: OnMessageHandler;
|
||||
public onMessageDelivered: OnMessageHandler;
|
||||
/** <i>read only</i> the server's DNS hostname or dotted decimal IP address. */
|
||||
readonly host: string;
|
||||
|
||||
// tslint:disable unified-signatures
|
||||
/** <i>read only</i> the server's path. */
|
||||
readonly path: string;
|
||||
|
||||
/** <i>read only</i> the server's port. */
|
||||
readonly port: number;
|
||||
|
||||
/** function called with trace information, if set */
|
||||
trace?: TraceFunction;
|
||||
|
||||
/**
|
||||
* called when a connection has been lost. after a connect() method has succeeded.
|
||||
* Establish the call back used when a connection has been lost. The connection may be
|
||||
* lost because the client initiates a disconnect or because the server or network
|
||||
* cause the client to be disconnected. The disconnect call back may be called without
|
||||
* the connectionComplete call back being invoked if, for example the client fails to
|
||||
* connect.
|
||||
* A single response object parameter is passed to the onConnectionLost callback containing the following
|
||||
* fields:
|
||||
* <li>errorCode
|
||||
* <li>errorMessage
|
||||
*/
|
||||
onConnectionLost: OnConnectionLostHandler;
|
||||
|
||||
/**
|
||||
* called when a message has been delivered.
|
||||
* All processing that this Client will ever do has been completed. So, for example,
|
||||
* in the case of a Qos=2 message sent by this client, the PubComp flow has been received from the server
|
||||
* and the message has been removed from persistent storage before this callback is invoked.
|
||||
* Parameters passed to the onMessageDelivered callback are:
|
||||
* <li>{@link Paho.MQTT.Message} that was delivered.
|
||||
*/
|
||||
onMessageDelivered: OnMessageHandler;
|
||||
|
||||
/**
|
||||
* called when a message has arrived in this Paho.MQTT.client.
|
||||
* Parameters passed to the onMessageArrived callback are:
|
||||
* <li> {@link Paho.MQTT.Message} that has arrived.
|
||||
*/
|
||||
onMessageArrived: OnMessageHandler;
|
||||
|
||||
/* tslint:disable:unified-signatures */
|
||||
/* these cannot actually be neatly unified */
|
||||
|
||||
/**
|
||||
* @param host - the address of the messaging server as a DNS name or dotted decimal IP address.
|
||||
* @param port - the port number to connect to
|
||||
* @param path - the path on the host to connect to - only used if host is not a URI. Default: '/mqtt'.
|
||||
* @param clientId - the Messaging client identifier, between 1 and 23 characters in length.
|
||||
*/
|
||||
constructor(host: string, port: number, path: string, clientId: string);
|
||||
|
||||
/**
|
||||
* @param host - the address of the messaging server as a DNS name or dotted decimal IP address.
|
||||
* @param port - the port number to connect to
|
||||
* @param clientId - the Messaging client identifier, between 1 and 23 characters in length.
|
||||
*/
|
||||
constructor(host: string, port: number, clientId: string);
|
||||
|
||||
/**
|
||||
* @param hostUri - the address of the messaging server as a fully qualified WebSocket URI
|
||||
* @param clientId - the Messaging client identifier, between 1 and 23 characters in length.
|
||||
*/
|
||||
constructor(hostUri: string, clientId: string);
|
||||
|
||||
public connect(connectionOptions?: ConnectionOptions);
|
||||
public disconnect();
|
||||
/* tslint:enable:unified-signatures */
|
||||
|
||||
public getTraceLog(): object[];
|
||||
public startTrace();
|
||||
public stopTrace();
|
||||
/**
|
||||
* Connect this Messaging client to its server.
|
||||
* @throws {InvalidState} if the client is not in disconnected state. The client must have received
|
||||
* connectionLost or disconnected before calling connect for a second or subsequent time.
|
||||
*/
|
||||
connect(connectionOptions?: ConnectionOptions): void;
|
||||
|
||||
public send(message: Message);
|
||||
public subscribe(filter: string, subcribeOptions?: SubscribeOptions);
|
||||
public unsubscribe(filter: string, unsubcribeOptions?: UnsubscribeOptions);
|
||||
/**
|
||||
* Normal disconnect of this Messaging client from its server.
|
||||
*
|
||||
* @throws {InvalidState} if the client is already disconnected.
|
||||
*/
|
||||
disconnect(): void;
|
||||
|
||||
/**
|
||||
* @returns True if the client is currently connected
|
||||
*/
|
||||
isConnected(): boolean;
|
||||
|
||||
/**
|
||||
* Get the contents of the trace log.
|
||||
*
|
||||
* @return {Object[]} tracebuffer containing the time ordered trace records.
|
||||
*/
|
||||
getTraceLog(): any[];
|
||||
|
||||
/**
|
||||
* Start tracing.
|
||||
*/
|
||||
startTrace(): void;
|
||||
|
||||
/**
|
||||
* Stop tracing.
|
||||
*/
|
||||
stopTrace(): void;
|
||||
|
||||
/**
|
||||
* Send a message to the consumers of the destination in the Message.
|
||||
*
|
||||
* @param {Paho.MQTT.Message} message - <b>mandatory</b> The {@link Paho.MQTT.Message} object to be sent.
|
||||
* @throws {InvalidState} if the client is not connected.
|
||||
*/
|
||||
send(message: Message): void;
|
||||
|
||||
/**
|
||||
* Send a message to the consumers of the destination in the Message.
|
||||
*
|
||||
* @param {string} topic - <b>mandatory</b> The name of the destination to which the message is to be sent.
|
||||
* @param {string|ArrayBuffer} payload - The message data to be sent.
|
||||
* @param {number} qos The Quality of Service used to deliver the message.
|
||||
* <dl>
|
||||
* <dt>0 Best effort (default).
|
||||
* <dt>1 At least once.
|
||||
* <dt>2 Exactly once.
|
||||
* </dl>
|
||||
* @param {Boolean} retained If true, the message is to be retained by the server and delivered to both
|
||||
* current and future subscriptions. If false the server only delivers the message to current subscribers,
|
||||
* this is the default for new Messages. A received message has the retained boolean set to true if the
|
||||
* message was published with the retained boolean set to true and the subscrption was made after the
|
||||
* message has been published.
|
||||
* @throws {InvalidState} if the client is not connected.
|
||||
*/
|
||||
send(topic: string, payload: string | ArrayBuffer, qos?: Qos, retained?: boolean): void;
|
||||
|
||||
/**
|
||||
* Subscribe for messages, request receipt of a copy of messages sent to the destinations described by the
|
||||
* filter.
|
||||
*
|
||||
* @param filter A filter describing the destinations to receive messages from.
|
||||
* @param subcribeOptions Used to control the subscription
|
||||
* @throws {InvalidState} if the client is not in connected state.
|
||||
*/
|
||||
subscribe(filter: string, subcribeOptions?: SubscribeOptions): void;
|
||||
|
||||
/**
|
||||
* Unsubscribe for messages, stop receiving messages sent to destinations described by the filter.
|
||||
*
|
||||
* @param filter - describing the destinations to receive messages from.
|
||||
* @param unsubscribeOptions - used to control the subscription
|
||||
* @throws {InvalidState} if the client is not in connected state.
|
||||
*/
|
||||
unsubscribe(filter: string, unsubcribeOptions?: UnsubscribeOptions): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* An application message, sent or received.
|
||||
*/
|
||||
class Message {
|
||||
public destinationName: string;
|
||||
public readonly duplicate: boolean;
|
||||
public readonly payloadBytes: ArrayBuffer;
|
||||
public readonly payloadString: string;
|
||||
public qos: number;
|
||||
public retained: boolean;
|
||||
/**
|
||||
* <b>mandatory</b> The name of the destination to which the message is to be sent
|
||||
* (for messages about to be sent) or the name of the destination from which the message has been received.
|
||||
* (for messages received by the onMessage function).
|
||||
*/
|
||||
destinationName?: string;
|
||||
|
||||
/**
|
||||
* <i>read only</i> If true, this message might be a duplicate of one which has already been received.
|
||||
* This is only set on messages received from the server.
|
||||
*/
|
||||
readonly duplicate: boolean;
|
||||
|
||||
/** <i>read only</i> The payload as an ArrayBuffer. */
|
||||
readonly payloadBytes: ArrayBuffer;
|
||||
|
||||
/**
|
||||
* <i>read only</i> The payload as a string if the payload consists of valid UTF-8 characters.
|
||||
* @throw {Error} if the payload is not valid UTF-8
|
||||
*/
|
||||
readonly payloadString: string;
|
||||
|
||||
/**
|
||||
* The Quality of Service used to deliver the message.
|
||||
* <dl>
|
||||
* <dt>0 Best effort (default).
|
||||
* <dt>1 At least once.
|
||||
* <dt>2 Exactly once.
|
||||
* </dl>
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
qos: number;
|
||||
|
||||
/**
|
||||
* If true, the message is to be retained by the server and delivered to both current and future
|
||||
* subscriptions. If false the server only delivers the message to current subscribers, this is the default
|
||||
* for new Messages. A received message has the retained boolean set to true if the message was published
|
||||
* with the retained boolean set to true and the subscription was made after the message has been published.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
retained: boolean;
|
||||
|
||||
/**
|
||||
* @param {String|ArrayBuffer} payload The message data to be sent.
|
||||
*/
|
||||
constructor(payload: string | ArrayBuffer);
|
||||
}
|
||||
}
|
||||
|
@ -120,6 +120,10 @@ export class Program {
|
||||
}
|
||||
}
|
||||
|
||||
export class SectionRunner {
|
||||
|
||||
}
|
||||
|
||||
export abstract class SprinklersDevice {
|
||||
@observable
|
||||
public connected: boolean = false;
|
||||
|
44
tslint.json
44
tslint.json
@ -1,25 +1,25 @@
|
||||
{
|
||||
"defaultSeverity": "error",
|
||||
"extends": [
|
||||
"tslint:recommended"
|
||||
"defaultSeverity": "error",
|
||||
"extends": [
|
||||
"tslint:latest"
|
||||
],
|
||||
"jsRules": {},
|
||||
"rules": {
|
||||
"no-console": [
|
||||
false
|
||||
],
|
||||
"jsRules": {},
|
||||
"rules": {
|
||||
"no-console": [
|
||||
false
|
||||
],
|
||||
"max-classes-per-file": [
|
||||
false
|
||||
],
|
||||
"ordered-imports": [
|
||||
false
|
||||
],
|
||||
"variable-name": [
|
||||
"allow-leading-underscore"
|
||||
],
|
||||
"no-namespace": [
|
||||
"allow-declarations"
|
||||
]
|
||||
},
|
||||
"rulesDirectory": []
|
||||
"max-classes-per-file": [
|
||||
false
|
||||
],
|
||||
"ordered-imports": false,
|
||||
"variable-name": [
|
||||
"allow-leading-underscore"
|
||||
],
|
||||
"no-namespace": [
|
||||
"allow-declarations"
|
||||
],
|
||||
"interface-name": false,
|
||||
"member-access": [ true, "no-public" ]
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user