mqtt-control-map/src/components/App.js

152 lines
4.4 KiB
JavaScript

// @flow
import React from "react";
import map from "lodash/map";
import mapValues from "lodash/mapValues";
import filter from "lodash/filter";
import keys from "lodash/keys";
import merge from "lodash/merge";
import type { Config, Control, Topics } from "config/flowtypes";
import MuiThemeProvider from "@material-ui/core/styles/MuiThemeProvider";
import createMuiTheme from "@material-ui/core/styles/createMuiTheme";
import withStyles from "@material-ui/core/styles/withStyles";
import * as Colors from "@material-ui/core/colors";
import SideBar from "components/SideBar";
import ControlMap from "components/ControlMap";
import TopBar from "components/TopBar";
import UiItemList from "components/UiItemList";
import keyOf from "utils/keyOf";
import { toRawIcon } from "config/icon";
import connectMqtt from "../connectMqtt";
export type AppProps = {
config: Config
};
export type AppState = {
selectedControl: ?Control,
drawerOpened: boolean,
mqttState: State,
mqttSend: (topic: string, value: Actual) => void,
mqttConnected: boolean,
};
class App extends React.PureComponent<AppProps & Classes, AppState> {
constructor(props: AppProps & Classes) {
super(props);
this.state = {
selectedControl: null,
drawerOpened: false,
mqttState: mapValues(this.topics, (topic) => ({
actual: topic.defaultValue,
internal: keyOf(topic.values, topic.defaultValue)
})),
mqttSend: connectMqtt(props.config.space.mqtt, {
onMessage: this.receiveMessage.bind(this),
onConnect: () => this.setState({ mqttConnected: true }),
onReconnect: () => this.setState({ mqttConnected: false }),
onDisconnect: () => this.setState({ mqttConnected: false }),
subscribe: map(this.topics, (x) => x.state)
}),
mqttConnected: false
};
}
get topics(): Topics {
return Array.isArray(this.props.config.topics) ?
Object.assign({}, ...this.props.config.topics) : this.props.config.topics;
}
static styles(_theme: Object) {
return {
drawerPaper: {
width: 320
}
};
}
get theme() {
return createMuiTheme({
palette: {
primary: Colors[this.props.config.space.color]
}
});
}
receiveMessage(rawTopic: string, message: Object) {
const topics = filter(
keys(this.topics),
(k) => this.topics[k].state === rawTopic
);
if (topics.length === 0) {
return;
}
for (let i in topics) {
const topic = topics[i];
const parseValue = this.topics[topic].type;
const val = parseValue == null ? message.toString() : parseValue(message);
this.setState({mqttState: Object.assign({}, merge(this.state.mqttState,
{ [topic]: {
actual: val,
internal: keyOf(this.topics[topic].values, val) || val
}}))});
}
}
changeControl(control: ?Control = null) {
this.setState({selectedControl: control, drawerOpened: control != null});
}
closeDrawer() {
this.setState({drawerOpened: false});
}
changeState(topic: string, value: Actual) {
const rawTopic = this.topics[topic].command;
if (rawTopic == null) {
return;
}
this.state.mqttSend(
rawTopic,
String(this.topics[topic].values[value] || value)
);
}
render() {
return (
<div>
<MuiThemeProvider theme={this.theme}>
<div>
<TopBar title={`${this.props.config.space.name} Map`}
connected={this.state.mqttConnected} />
<SideBar open={this.state.drawerOpened}
control={this.state.selectedControl}
onCloseRequest={this.closeDrawer.bind(this)}
icon={this.state.selectedControl == null ? null :
toRawIcon(this.state.selectedControl.icon,
this.state.mqttState)}
>
{this.state.selectedControl == null
|| <UiItemList state={this.state.mqttState}
controls={this.state.selectedControl.ui}
onChangeState={this.changeState.bind(this)}
/>}
</SideBar>
</div>
</MuiThemeProvider>
<ControlMap width={1000} height={700} zoom={0}
layers={this.props.config.layers}
controls={this.props.config.controls}
onChangeControl={this.changeControl.bind(this)}
state={this.state.mqttState}
/>
</div>
);
}
}
export default withStyles(App.styles)(App);