More awesome work on stuff
This commit is contained in:
parent
015614b1d6
commit
f9ae6fac6f
@ -4,7 +4,7 @@ import * as React from "react";
|
|||||||
import FontAwesome = require("react-fontawesome");
|
import FontAwesome = require("react-fontawesome");
|
||||||
import { Header, Item } from "semantic-ui-react";
|
import { Header, Item } from "semantic-ui-react";
|
||||||
|
|
||||||
import { injectState, State } from "@app/state";
|
import { injectState, MqttApiState } from "@app/state";
|
||||||
import { SprinklersDevice } from "@common/sprinklers";
|
import { SprinklersDevice } from "@common/sprinklers";
|
||||||
import { ProgramTable, RunSectionForm, SectionRunnerView, SectionTable } from ".";
|
import { ProgramTable, RunSectionForm, SectionRunnerView, SectionTable } from ".";
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ const ConnectionState = ({ connected }: { connected: boolean }) => {
|
|||||||
|
|
||||||
interface DeviceViewProps {
|
interface DeviceViewProps {
|
||||||
deviceId: string;
|
deviceId: string;
|
||||||
state: State;
|
state: MqttApiState;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeviceView extends React.Component<DeviceViewProps> {
|
class DeviceView extends React.Component<DeviceViewProps> {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Button, Segment } from "semantic-ui-react";
|
import { Button, Segment } from "semantic-ui-react";
|
||||||
|
|
||||||
import { injectState, State } from "@app/state";
|
import { injectState, MqttApiState } from "@app/state";
|
||||||
import { getRandomId } from "@common/utils";
|
import { getRandomId } from "@common/utils";
|
||||||
|
|
||||||
class MessageTest extends React.Component<{ state: State }> {
|
class MessageTest extends React.Component<{ state: MqttApiState }> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Segment>
|
<Segment>
|
||||||
|
@ -3,7 +3,7 @@ import { observer } from "mobx-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Message, MessageProps, TransitionGroup } from "semantic-ui-react";
|
import { Message, MessageProps, TransitionGroup } from "semantic-ui-react";
|
||||||
|
|
||||||
import { injectState, State, UiMessage, UiStore } from "@app/state/";
|
import { injectState, MqttApiState, UiMessage, UiStore } from "@app/state/";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
class MessageView extends React.Component<{
|
class MessageView extends React.Component<{
|
||||||
@ -33,7 +33,7 @@ class MessageView extends React.Component<{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessagesView extends React.Component<{ state: State }> {
|
class MessagesView extends React.Component<{ state: MqttApiState }> {
|
||||||
render() {
|
render() {
|
||||||
const { uiStore } = this.props.state;
|
const { uiStore } = this.props.state;
|
||||||
const messages = uiStore.messages.map((message) => (
|
const messages = uiStore.messages.map((message) => (
|
||||||
|
@ -3,12 +3,13 @@ import * as ReactDOM from "react-dom";
|
|||||||
import { AppContainer } from "react-hot-loader";
|
import { AppContainer } from "react-hot-loader";
|
||||||
|
|
||||||
import App from "@app/components/App";
|
import App from "@app/components/App";
|
||||||
import { ProvideState, State } from "@app/state";
|
import { ProvideState, MqttApiState, WebApiState } from "@app/state";
|
||||||
import log, { setLogger } from "@common/logger";
|
import log, { setLogger } from "@common/logger";
|
||||||
|
|
||||||
setLogger(log.child({ name: "sprinklers3/app" }));
|
setLogger(log.child({ name: "sprinklers3/app" }));
|
||||||
|
|
||||||
const state = new State();
|
// const state = new MqttApiState();
|
||||||
|
const state = new WebApiState();
|
||||||
state.start();
|
state.start();
|
||||||
|
|
||||||
const rootElem = document.getElementById("app");
|
const rootElem = document.getElementById("app");
|
||||||
|
@ -1,17 +1,13 @@
|
|||||||
import { ISprinklersApi } from "@common/sprinklers";
|
import { ISprinklersApi } from "@common/sprinklers";
|
||||||
import { MqttApiClient } from "@common/sprinklers/mqtt";
|
import { MqttApiClient } from "@common/sprinklers/mqtt";
|
||||||
|
import { WebApiClient } from "./web";
|
||||||
|
|
||||||
import { UiMessage, UiStore } from "./ui";
|
import { UiMessage, UiStore } from "./ui";
|
||||||
export { UiMessage, UiStore };
|
export { UiMessage, UiStore };
|
||||||
export * from "./inject";
|
export * from "./inject";
|
||||||
|
|
||||||
export interface IState {
|
export abstract class StateBase {
|
||||||
sprinklersApi: ISprinklersApi;
|
abstract readonly sprinklersApi: ISprinklersApi;
|
||||||
uiStore: UiStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class State implements IState {
|
|
||||||
sprinklersApi: ISprinklersApi = new MqttApiClient(`ws://${location.hostname}:1884`);
|
|
||||||
uiStore = new UiStore();
|
uiStore = new UiStore();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -23,6 +19,14 @@ export class State implements IState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class MqttApiState extends StateBase {
|
||||||
|
sprinklersApi = new MqttApiClient(`ws://${location.hostname}:1884`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WebApiState extends StateBase {
|
||||||
|
sprinklersApi = new WebApiClient();
|
||||||
|
}
|
||||||
|
|
||||||
// const state = new State();
|
// const state = new State();
|
||||||
|
|
||||||
// export default state;
|
// export default state;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import * as PropTypes from "prop-types";
|
import * as PropTypes from "prop-types";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { State } from "@app/state";
|
import { StateBase } from "@app/state";
|
||||||
|
|
||||||
interface IProvidedStateContext {
|
interface IProvidedStateContext {
|
||||||
providedState: State;
|
providedState: StateBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
const providedStateContextTypes: PropTypes.ValidationMap<any> = {
|
const providedStateContextTypes: PropTypes.ValidationMap<any> = {
|
||||||
@ -12,7 +12,7 @@ const providedStateContextTypes: PropTypes.ValidationMap<any> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class ProvideState extends React.Component<{
|
export class ProvideState extends React.Component<{
|
||||||
state: State,
|
state: StateBase,
|
||||||
}> implements React.ChildContextProvider<IProvidedStateContext> {
|
}> implements React.ChildContextProvider<IProvidedStateContext> {
|
||||||
static childContextTypes = providedStateContextTypes;
|
static childContextTypes = providedStateContextTypes;
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ export class ProvideState extends React.Component<{
|
|||||||
type Diff<T extends string, U extends string> = ({[P in T]: P } & {[P in U]: never } & { [x: string]: never })[T];
|
type Diff<T extends string, U extends string> = ({[P in T]: P } & {[P in U]: never } & { [x: string]: never })[T];
|
||||||
type Omit<T, K extends keyof T> = {[P in Diff<keyof T, K>]: T[P]};
|
type Omit<T, K extends keyof T> = {[P in Diff<keyof T, K>]: T[P]};
|
||||||
|
|
||||||
export function injectState<P extends { "state": State }>(Component: React.ComponentType<P>) {
|
export function injectState<P extends { "state": StateBase }>(Component: React.ComponentType<P>) {
|
||||||
return class extends React.Component<Omit<P, "state">> {
|
return class extends React.Component<Omit<P, "state">> {
|
||||||
static contextTypes = providedStateContextTypes;
|
static contextTypes = providedStateContextTypes;
|
||||||
context: IProvidedStateContext;
|
context: IProvidedStateContext;
|
||||||
|
46
app/state/web.ts
Normal file
46
app/state/web.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { update } from "serializr";
|
||||||
|
|
||||||
|
import * as s from "@common/sprinklers";
|
||||||
|
import * as schema from "@common/sprinklers/json";
|
||||||
|
|
||||||
|
export class WebSprinklersDevice extends s.SprinklersDevice {
|
||||||
|
get id() {
|
||||||
|
return "grinklers";
|
||||||
|
}
|
||||||
|
async runSection(section: number | s.Section, duration: s.Duration): Promise<{}> {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
async runProgram(program: number | s.Program): Promise<{}> {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
async cancelSectionRunById(id: number): Promise<{}> {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
async pauseSectionRunner(): Promise<{}> {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
async unpauseSectionRunner(): Promise<{}> {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WebApiClient implements s.ISprinklersApi {
|
||||||
|
start() {
|
||||||
|
// NOT IMPLEMENTED
|
||||||
|
}
|
||||||
|
|
||||||
|
getDevice(name: string): s.SprinklersDevice {
|
||||||
|
const device = new WebSprinklersDevice();
|
||||||
|
fetch("/api/grinklers")
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((json) => {
|
||||||
|
update(schema.sprinklersDeviceSchema, device, json);
|
||||||
|
})
|
||||||
|
.catch((e) => alert(e));
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeDevice(name: string) {
|
||||||
|
// NOT IMPLEMENTED
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,10 @@ export abstract class SprinklersDevice {
|
|||||||
@observable programs: Program[] = [];
|
@observable programs: Program[] = [];
|
||||||
@observable sectionRunner: SectionRunner;
|
@observable sectionRunner: SectionRunner;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.sectionRunner = new (this.sectionRunnerConstructor)(this);
|
||||||
|
}
|
||||||
|
|
||||||
abstract get id(): string;
|
abstract get id(): string;
|
||||||
abstract runSection(section: number | Section, duration: Duration): Promise<{}>;
|
abstract runSection(section: number | Section, duration: Duration): Promise<{}>;
|
||||||
abstract runProgram(program: number | Program): Promise<{}>;
|
abstract runProgram(program: number | Program): Promise<{}>;
|
||||||
@ -17,9 +21,15 @@ export abstract class SprinklersDevice {
|
|||||||
abstract pauseSectionRunner(): Promise<{}>;
|
abstract pauseSectionRunner(): Promise<{}>;
|
||||||
abstract unpauseSectionRunner(): Promise<{}>;
|
abstract unpauseSectionRunner(): Promise<{}>;
|
||||||
|
|
||||||
abstract get sectionConstructor(): typeof Section;
|
get sectionConstructor(): typeof Section {
|
||||||
abstract get sectionRunnerConstructor(): typeof SectionRunner;
|
return Section;
|
||||||
abstract get programConstructor(): typeof Program;
|
}
|
||||||
|
get sectionRunnerConstructor(): typeof SectionRunner {
|
||||||
|
return SectionRunner;
|
||||||
|
}
|
||||||
|
get programConstructor(): typeof Program {
|
||||||
|
return Program;
|
||||||
|
}
|
||||||
|
|
||||||
toString(): string {
|
toString(): string {
|
||||||
return `SprinklersDevice{id="${this.id}", connected=${this.connected}, ` +
|
return `SprinklersDevice{id="${this.id}", connected=${this.connected}, ` +
|
||||||
|
@ -8,15 +8,28 @@ import * as s from "..";
|
|||||||
export const durationSchema: PropSchema = {
|
export const durationSchema: PropSchema = {
|
||||||
serializer: (duration: s.Duration | null) =>
|
serializer: (duration: s.Duration | null) =>
|
||||||
duration != null ? duration.toSeconds() : null,
|
duration != null ? duration.toSeconds() : null,
|
||||||
deserializer: (json: any) =>
|
deserializer: (json: any, done) => {
|
||||||
typeof json === "number" ? s.Duration.fromSeconds(json) : null,
|
if (typeof json === "number") {
|
||||||
|
done(null, s.Duration.fromSeconds(json));
|
||||||
|
} else {
|
||||||
|
done(new Error(`Duration expects a number, not ${json}`), undefined);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const dateSchema: PropSchema = {
|
export const dateSchema: PropSchema = {
|
||||||
serializer: (jsDate: Date | null) => jsDate != null ?
|
serializer: (jsDate: Date | null) => jsDate != null ?
|
||||||
jsDate.toISOString() : null,
|
jsDate.toISOString() : null,
|
||||||
deserializer: (json: any) => typeof json === "string" ?
|
deserializer: (json: any, done) => {
|
||||||
new Date(json) : null,
|
if (json === null) {
|
||||||
|
done(null, null);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
done(null, new Date(json));
|
||||||
|
} catch (e) {
|
||||||
|
done(e, undefined);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const dateOfYearSchema: ModelSchema<s.DateOfYear> = {
|
export const dateOfYearSchema: ModelSchema<s.DateOfYear> = {
|
||||||
|
@ -13,13 +13,17 @@ const mqttClient = new mqtt.MqttApiClient("mqtt://localhost:1883");
|
|||||||
mqttClient.start();
|
mqttClient.start();
|
||||||
|
|
||||||
import { sprinklersDeviceSchema } from "@common/sprinklers/json";
|
import { sprinklersDeviceSchema } from "@common/sprinklers/json";
|
||||||
import { autorun } from "mobx";
|
import { autorunAsync } from "mobx";
|
||||||
import { serialize } from "serializr";
|
import { serialize } from "serializr";
|
||||||
const device = mqttClient.getDevice("grinklers");
|
const device = mqttClient.getDevice("grinklers");
|
||||||
|
|
||||||
|
autorunAsync(() => {
|
||||||
|
const j = serialize(sprinklersDeviceSchema, device);
|
||||||
|
log.info({ device: j });
|
||||||
|
}, 0);
|
||||||
|
|
||||||
app.get("/api/grinklers", (req, res) => {
|
app.get("/api/grinklers", (req, res) => {
|
||||||
const j = serialize(sprinklersDeviceSchema, device);
|
const j = serialize(sprinklersDeviceSchema, device);
|
||||||
log.trace(j);
|
|
||||||
res.send(j);
|
res.send(j);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user