Merge remote-tracking branch 'origin/master' into fork/patch-41
This commit is contained in:
commit
c298a58a47
13 changed files with 655 additions and 621 deletions
|
|
@ -7,7 +7,8 @@ module.exports = {
|
||||||
"extends": [
|
"extends": [
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
"plugin:flowtype/recommended",
|
"plugin:flowtype/recommended",
|
||||||
"plugin:react/recommended"
|
"plugin:react/recommended",
|
||||||
|
"plugin:react-hooks/recommended"
|
||||||
],
|
],
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaFeatures": {
|
"ecmaFeatures": {
|
||||||
|
|
@ -161,6 +162,6 @@ module.exports = {
|
||||||
"fp/no-throw": "warn",
|
"fp/no-throw": "warn",
|
||||||
"fp/no-unused-expression": "warn",
|
"fp/no-unused-expression": "warn",
|
||||||
"fp/no-valueof-field": "warn",
|
"fp/no-valueof-field": "warn",
|
||||||
"no-var": "warn"
|
"no-var": "warn"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ const sliderSVXY = (bulb: string, argument: string) => (
|
||||||
const config: Config = {
|
const config: Config = {
|
||||||
space: {
|
space: {
|
||||||
name: "Home",
|
name: "Home",
|
||||||
color: "orange",
|
color: "teal",
|
||||||
mqtt: "ws://192.168.0.12:1884"
|
mqtt: "ws://192.168.0.12:1884"
|
||||||
},
|
},
|
||||||
topics: [
|
topics: [
|
||||||
|
|
@ -530,6 +530,11 @@ const config: Config = {
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 255,
|
max: 255,
|
||||||
text: "Helligkeit",
|
text: "Helligkeit",
|
||||||
|
marks: [
|
||||||
|
{ value: 1, label: "Dunkel" },
|
||||||
|
{ value: 120, label: "Medium" },
|
||||||
|
{ value: 254, label: "Hell" }
|
||||||
|
],
|
||||||
icon: svg(icons.mdiBrightness7),
|
icon: svg(icons.mdiBrightness7),
|
||||||
topic: "livingroombrightness"
|
topic: "livingroombrightness"
|
||||||
},
|
},
|
||||||
|
|
@ -644,7 +649,7 @@ const config: Config = {
|
||||||
{
|
{
|
||||||
image: require("./assets/layers/rooms.svg"),
|
image: require("./assets/layers/rooms.svg"),
|
||||||
baseLayer: true,
|
baseLayer: true,
|
||||||
name: "Uwap Home",
|
name: "Rooms",
|
||||||
defaultVisibility: "visible",
|
defaultVisibility: "visible",
|
||||||
opacity: 0.7,
|
opacity: 0.7,
|
||||||
bounds: {
|
bounds: {
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@
|
||||||
"eslint-plugin-flowtype": "^5.2.0",
|
"eslint-plugin-flowtype": "^5.2.0",
|
||||||
"eslint-plugin-fp": "^2.3.0",
|
"eslint-plugin-fp": "^2.3.0",
|
||||||
"eslint-plugin-react": "^7.14.3",
|
"eslint-plugin-react": "^7.14.3",
|
||||||
|
"eslint-plugin-react-hooks": "^4.1.2",
|
||||||
"file-loader": "^6.1.0",
|
"file-loader": "^6.1.0",
|
||||||
"flow": "^0.2.3",
|
"flow": "^0.2.3",
|
||||||
"flow-bin": "^0.135.0",
|
"flow-bin": "^0.135.0",
|
||||||
|
|
|
||||||
|
|
@ -124,8 +124,8 @@ class App extends React.PureComponent<AppProps & Classes, AppState> {
|
||||||
for (let i in topics) {
|
for (let i in topics) {
|
||||||
const topic = topics[i];
|
const topic = topics[i];
|
||||||
const stateTopic = this.topics[topic].state;
|
const stateTopic = this.topics[topic].state;
|
||||||
const parseVal = stateTopic ? stateTopic.type : null;
|
const typeConversion = stateTopic?.type?.from ?? stateTopic?.type;
|
||||||
const val = parseVal == null ? message.toString() : parseVal(message);
|
const val = (typeConversion ?? ((x: Buffer) => x.toString()))(message);
|
||||||
this.setMqttStateDebounced(
|
this.setMqttStateDebounced(
|
||||||
{mqttState: Object.assign({},
|
{mqttState: Object.assign({},
|
||||||
merge(this.state.mqttState, { [topic]: val}))});
|
merge(this.state.mqttState, { [topic]: val}))});
|
||||||
|
|
@ -145,16 +145,16 @@ class App extends React.PureComponent<AppProps & Classes, AppState> {
|
||||||
this.setState({drawerOpened: false});
|
this.setState({drawerOpened: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
changeState = (topic: string, value: string) => {
|
changeState = (topic: string, val: string) => {
|
||||||
try {
|
try {
|
||||||
if (this.topics[topic].command == null) {
|
const commandTopic = this.topics[topic].command;
|
||||||
|
if (commandTopic == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const rawTopic = this.topics[topic].command.name;
|
const rawTopic = commandTopic.name;
|
||||||
const transformValue = this.topics[topic].command.type;
|
const typeConversion = commandTopic?.type?.to ?? commandTopic.type;
|
||||||
const val =
|
const value = (typeConversion ?? Buffer.from)(val);
|
||||||
transformValue == null ? value : transformValue(Buffer.from(value));
|
this.state.mqttSend(rawTopic, value);
|
||||||
this.state.mqttSend(rawTopic, Buffer.from(val));
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.setState({ error: err.toString() });
|
this.setState({ error: err.toString() });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@ import map from "lodash/map";
|
||||||
import filter from "lodash/filter";
|
import filter from "lodash/filter";
|
||||||
import reduce from "lodash/reduce";
|
import reduce from "lodash/reduce";
|
||||||
import MqttContext from "mqtt/context";
|
import MqttContext from "mqtt/context";
|
||||||
import type { Controls, Control, UIControl, ControlUI } from "config/flowtypes";
|
import type {
|
||||||
|
Controls, Control, UIControl, ControlUI, Layer
|
||||||
|
} from "config/flowtypes";
|
||||||
import { renderToString } from "react-dom/server";
|
import { renderToString } from "react-dom/server";
|
||||||
|
|
||||||
export type Point = [number, number];
|
export type Point = [number, number];
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { makeStyles } from "@material-ui/core/styles";
|
||||||
import Tooltip from "@material-ui/core/Tooltip";
|
import Tooltip from "@material-ui/core/Tooltip";
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
import ReactIcon from "@mdi/react";
|
import ReactIcon from "@mdi/react";
|
||||||
import { mdiMap, mdiGithub } from "@mdi/js";
|
import { mdiMap, mdiGithub, mdiMagnify } from "@mdi/js";
|
||||||
|
|
||||||
export type TopBarProps = {
|
export type TopBarProps = {
|
||||||
connected: boolean,
|
connected: boolean,
|
||||||
|
|
@ -77,7 +77,9 @@ const Search = (props: SearchBarProps) => {
|
||||||
const classes = useSearchStyles();
|
const classes = useSearchStyles();
|
||||||
return (
|
return (
|
||||||
<div className={classes.search}>
|
<div className={classes.search}>
|
||||||
<i className={`mdi mdi-magnify ${classes.searchIcon}`}></i>
|
<span className={classes.searchIcon}>
|
||||||
|
<ReactIcon path={mdiMagnify} size={1} />
|
||||||
|
</span>
|
||||||
<InputBase placeholder="Search…" type="search"
|
<InputBase placeholder="Search…" type="search"
|
||||||
onChange={(e) => props.onSearch(e.target.value)}
|
onChange={(e) => props.onSearch(e.target.value)}
|
||||||
classes={{
|
classes={{
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,12 @@ const BaseComponent = ({Icon, Label}, item, state, changeState) => (
|
||||||
<Label />
|
<Label />
|
||||||
<SliderComponent
|
<SliderComponent
|
||||||
value={parseFloat(getValue(item, state))}
|
value={parseFloat(getValue(item, state))}
|
||||||
min={item.min || 0} max={item.max || 100}
|
min={item.min ?? 0} max={item.max ?? 100}
|
||||||
step={item.step || 1}
|
step={item.step}
|
||||||
|
marks={item.marks ?? false}
|
||||||
onChange={changeSliderValue(item, changeState)}
|
onChange={changeSliderValue(item, changeState)}
|
||||||
disabled={isDisabled(item, state)}
|
disabled={isDisabled(item, state)}
|
||||||
|
valueLabelDisplay="auto"
|
||||||
style={{marginLeft: 40}} />
|
style={{marginLeft: 40}} />
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,22 @@
|
||||||
// @flow
|
// @flow
|
||||||
import type { Icon } from "config/icon";
|
import type { Icon } from "config/icon";
|
||||||
|
|
||||||
export type TopicType = (msg: Buffer) => string;
|
export type TopicType = {
|
||||||
|
from: (msg: Buffer) => string,
|
||||||
|
to: (newstate: string) => Buffer
|
||||||
|
};
|
||||||
|
|
||||||
export type StateCommand = {
|
export type StateTopicType = TopicType | ((msg: Buffer) => string);
|
||||||
|
export type CommandTopicType = TopicType | ((newstate: string) => Buffer);
|
||||||
|
|
||||||
|
export type StateCommand<T> = {
|
||||||
name: string,
|
name: string,
|
||||||
type: TopicType
|
type: T
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Topic = {
|
export type Topic = {
|
||||||
state?: StateCommand,
|
state?: StateCommand<StateTopicType>,
|
||||||
command?: StateCommand,
|
command?: StateCommand<CommandTopicType>,
|
||||||
defaultValue: string
|
defaultValue: string
|
||||||
};
|
};
|
||||||
export type Topics = Map<string, Topic>;
|
export type Topics = Map<string, Topic>;
|
||||||
|
|
@ -52,9 +58,10 @@ export type UISlider = $ReadOnly<{|
|
||||||
topic: string,
|
topic: string,
|
||||||
icon?: Icon,
|
icon?: Icon,
|
||||||
enableCondition?: (s: State) => boolean,
|
enableCondition?: (s: State) => boolean,
|
||||||
|
marks?: boolean | Array<{ value: number, label: string}>,
|
||||||
min?: number,
|
min?: number,
|
||||||
max?: number,
|
max?: number,
|
||||||
step?: number
|
step?: ?number
|
||||||
|}>;
|
|}>;
|
||||||
|
|
||||||
export type UISection = $ReadOnly<{|
|
export type UISection = $ReadOnly<{|
|
||||||
|
|
@ -114,6 +121,17 @@ export type Space = {
|
||||||
mqtt: string
|
mqtt: string
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Layer = {
|
||||||
|
image: string,
|
||||||
|
name: string,
|
||||||
|
baseLayer?: boolean,
|
||||||
|
defaultVisibility: "visible" | "hidden",
|
||||||
|
opacity?: number,
|
||||||
|
bounds: {
|
||||||
|
topLeft: Point,
|
||||||
|
bottomRight: Point
|
||||||
|
}
|
||||||
|
};
|
||||||
export type Config = {
|
export type Config = {
|
||||||
space: Space,
|
space: Space,
|
||||||
topics: Topics | Array<Topics>,
|
topics: Topics | Array<Topics>,
|
||||||
|
|
|
||||||
|
|
@ -34,12 +34,12 @@ const iconChainUtils = <T> (cb: (x: T, p?: IconPropHelper) => Icon,
|
||||||
);
|
);
|
||||||
|
|
||||||
export const svg = (data: string, props?: IconPropHelper): Icon => {
|
export const svg = (data: string, props?: IconPropHelper): Icon => {
|
||||||
const propColor = ((c: Color | (State) => Color) => (state: State) => {
|
const propColor = ((c: ?Color | (State) => Color) => (state: State) => {
|
||||||
if (typeof c === "function") {
|
if (typeof c === "function") {
|
||||||
return c(state);
|
return c(state);
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
})(props?.color ?? "black");
|
})(props?.color);
|
||||||
return {
|
return {
|
||||||
render: (state) => (
|
render: (state) => (
|
||||||
<ReactIcon path={data} size={props?.size ?? 1.5}
|
<ReactIcon path={data} size={props?.size ?? 1.5}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,22 @@
|
||||||
// @flow
|
// @flow
|
||||||
import type { TopicType } from "config/flowtypes";
|
import type { TopicType } from "config/flowtypes";
|
||||||
import at from "lodash/at";
|
import at from "lodash/at";
|
||||||
|
import set from "lodash/set";
|
||||||
|
|
||||||
export const string: TopicType = (msg: Buffer) => msg.toString();
|
export const string: TopicType = {
|
||||||
|
from: (msg: Buffer) => msg.toString(),
|
||||||
|
to: (msg: string) => Buffer.from(msg)
|
||||||
|
};
|
||||||
|
|
||||||
export const json = (path: string, innerType?: TopicType): TopicType => {
|
export const json = (path: string, innerType?: TopicType): TopicType => {
|
||||||
const parseAgain = innerType == null ? (x) => x.toString() : innerType;
|
const parseAgain = innerType?.from ?? ((x) => x.toString());
|
||||||
return (msg) => parseAgain(Buffer.from(
|
const parseFirst = innerType?.to ?? ((x) => Buffer.from(x));
|
||||||
at(JSON.parse(msg.toString()), path)[0].toString()));
|
return {
|
||||||
|
from: (msg) => parseAgain(Buffer.from(
|
||||||
|
at(JSON.parse(msg.toString()), path)[0].toString())),
|
||||||
|
to: (msg) => Buffer.from(
|
||||||
|
JSON.stringify(set({}, path, parseFirst(msg).toString())))
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TypeOptionParam = { otherwise?: string, [string]: string };
|
export type TypeOptionParam = { otherwise?: string, [string]: string };
|
||||||
|
|
@ -16,13 +25,17 @@ export const option = (values: TypeOptionParam): TopicType => {
|
||||||
if (values.otherwise != null) {
|
if (values.otherwise != null) {
|
||||||
return values.otherwise;
|
return values.otherwise;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
return x;
|
||||||
`Value ${x.toString()} cannot be mapped by the option parameters given`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const mapVal = (x) => (values[x] != null ? values[x] : defaultValue(x));
|
const mapVal = (x) => (values[x] != null ? values[x] : defaultValue(x));
|
||||||
return (x) => mapVal(x.toString());
|
return {
|
||||||
|
from: (x) => mapVal(x.toString()),
|
||||||
|
to: (x) => Buffer.from(mapVal(x))
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const jsonArray = (msg: Buffer) => JSON.parse(msg.toString()).join(", ");
|
export const jsonArray = {
|
||||||
|
from: (msg: Buffer) => JSON.parse(msg.toString()).join(", "),
|
||||||
|
to: (msg: string) => Buffer.from(`[${msg}]`)
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,3 @@ declare type Classes = {
|
||||||
declare type State = Map<string,string>;
|
declare type State = Map<string,string>;
|
||||||
|
|
||||||
declare type Point = [number, number];
|
declare type Point = [number, number];
|
||||||
|
|
||||||
declare type Layer = {
|
|
||||||
image: string,
|
|
||||||
name: string,
|
|
||||||
baseLayer?: boolean,
|
|
||||||
defaultVisibility: "visible" | "hidden",
|
|
||||||
opacity?: number,
|
|
||||||
bounds: {
|
|
||||||
topLeft: Point,
|
|
||||||
bottomRight: Point
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,13 @@ module.exports = env => ({
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new CleanWebpackPlugin(),
|
new CleanWebpackPlugin(),
|
||||||
new WebpackShellPlugin({onBuildStart:preBuildScripts}),
|
new WebpackShellPlugin({
|
||||||
|
onBuildStart: {
|
||||||
|
scripts: preBuildScripts,
|
||||||
|
blocking: true,
|
||||||
|
parallel: false
|
||||||
|
}
|
||||||
|
}),
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
title: 'Space Map',
|
title: 'Space Map',
|
||||||
template: 'index.ejs'
|
template: 'index.ejs'
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue