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; | ||||
|  | ||||
							
								
								
									
										10
									
								
								tslint.json
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								tslint.json
									
									
									
									
									
								
							| @ -1,7 +1,7 @@ | ||||
| { | ||||
|   "defaultSeverity": "error", | ||||
|   "extends": [ | ||||
|         "tslint:recommended" | ||||
|     "tslint:latest" | ||||
|   ], | ||||
|   "jsRules": {}, | ||||
|   "rules": { | ||||
| @ -11,15 +11,15 @@ | ||||
|     "max-classes-per-file": [ | ||||
|       false | ||||
|     ], | ||||
|         "ordered-imports": [ | ||||
|             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