diff --git a/README.md b/README.md index 9756d72..eca38da 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ 1. run `yarn` to install all dependencies. 2. run `yarn watch CONFIG` to run a local build server that automatically builds your the mqtt control map for the given CONFIG everytime something changes. -3. run `yarn build CONFIG` to create just a single build of the mqtt control map +3. run `yarn dev CONFIG` to create just a single build of the mqtt control map for the given config. -4. run `yarn production-build CONFIG` to generate all files for production use. +4. run `yarn build CONFIG` to generate all files for production use. ## Config diff --git a/config/entropia.js b/config/entropia.js index 36e25d9..fc2f6ca 100644 --- a/config/entropia.js +++ b/config/entropia.js @@ -1,6 +1,7 @@ // @flow import type { Config } from "config/flowtypes"; import { hex, rgb, rgba, rainbow } from "config/colors"; +import { mdi } from "config/icon"; import { esper_topics, esper_statistics } from "./utils"; const config : Config = { @@ -29,20 +30,20 @@ const config : Config = { hauptraum_table_light: { name: "Hauptraum Tisch", position: [450, 450], - icon: "white-balance-iridescent", + icon: mdi("white-balance-iridescent"), iconColor: () => hex("#000000"), ui: [ { type: "toggle", text: "Licht", topic: "hauptraum_table_light", - icon: "power" + icon: mdi("power") }, { type: "toggle", text: "Licht", topic: "hauptraum_table_light_on_hack", - icon: "power" + icon: mdi("power") } ] } diff --git a/config/rzl.js b/config/rzl.js index b00e813..b515f95 100644 --- a/config/rzl.js +++ b/config/rzl.js @@ -2,6 +2,7 @@ import type { Config } from "config/flowtypes"; import * as types from "config/types"; import { hex, rgb, rgba, rainbow } from "config/colors"; +import { mdi, raw_mdi } from "config/icon"; import { esper_topics, esper_statistics, floalt, tradfri_remote } from "./utils"; const config : Config = { @@ -48,6 +49,12 @@ const config : Config = { defaultValue: "OFF", values: { on: "ON", off: "OFF" } }, + olymp_printer: { + state: "stat/sonoff2/POWER", + command: "cmnd/sonoff2/power", + defaultValue: "OFF", + values: { on: "ON", off: "OFF" } + }, flyfry: { state: "/service/openhab/out/wifi_flyfry/state", command: "/service/openhab/in/wifi_flyfry/command", @@ -204,63 +211,64 @@ const config : Config = { led_stahltrager: { name: "LED Stahlträger", position: [380, 590], - icon: "white-balance-iridescent", + icon: mdi("white-balance-iridescent"), iconColor: ({led_stahltraeger}) => led_stahltraeger == "on" ? rainbow : hex("#000000"), ui: [ { type: "toggle", text: "Stahlträger LED", topic: "led_stahltraeger", - icon: "power" + icon: mdi("power") }, ] }, snackbar: { name: "Snackbar", position: [510, 500], - icon: "fridge", + icon: mdi("fridge"), iconColor: ({snackbar}) => snackbar == "on" ? hex("#E20074") : hex("#000000"), ui: [ { type: "toggle", text: "Snackbar", topic: "snackbar", - icon: "power" + icon: mdi("power") } ] }, twinkle: { name: "Twinkle", position: [530, 560], - icon: ({twinkle}) => twinkle == "on" ? "led-on flip-v" : "led-off flip-v", + icon: ({twinkle}) => + twinkle == "on" ? raw_mdi("led-on flip-v") : raw_mdi("led-off flip-v"), iconColor: ({twinkle}) => twinkle == "on" ? rainbow : hex("#000000"), ui: [ { type: "toggle", text: "Twinkle", topic: "twinkle", - icon: "power" + icon: mdi("power") } ] }, fan: { name: "Ventilator", position: [520, 450], - icon: "fan", + icon: mdi("fan"), iconColor: ({fan}) => fan == "on" ? hex("#00FF00") : hex("#000000"), ui: [ { type: "toggle", text: "Ventilator", topic: "fan", - icon: "power" + icon: mdi("power") } ] }, cashdesk: { name: "Cashdesk", position: [500, 470], - icon: "coin", + icon: mdi("coin"), ui: [ { type: "link", @@ -272,49 +280,68 @@ const config : Config = { videogames: { name: "Videospiele", position: [100, 100], - icon: "gamepad-variant", + icon: mdi("gamepad-variant"), iconColor: ({videogames}) => videogames == "on" ? hex("#00FF00") : hex("#000000"), ui: [ { type: "toggle", text: "Videospiele", topic: "videogames", - icon: "power" + icon: mdi("power") } ] }, olymp_pc: { - name: "Rechner und Drucker", + name: "Rechner", position: [297, 90], - icon: "desktop-classic", + icon: mdi("desktop-classic"), iconColor: ({olymp_pc}) => olymp_pc == "on" ? hex("#00FF00") : hex("#000000"), ui: [ { type: "toggle", - text: "Rechner und Drucker", + text: "Rechner", topic: "olymp_pc", + icon: mdi("power") + } + ] + }, + olymp_printer: { + name: "Drucker", + position: [335, 90], + icon: "printer", + iconColor: ({olymp_printer}) => olymp_printer == "on" ? hex("#00FF00") : hex("#000000"), + ui: [ + { + type: "toggle", + text: "Drucker", + topic: "olymp_printer", icon: "power" + }, + { + type: "link", + link: "http://annette.rzl/", + text: "Open Annette" } ] }, flyfry: { name: "Fliegenbratgerät", position: [450, 590], - icon: "fire", + icon: mdi("fire"), iconColor: ({flyfry}) => flyfry == "on" ? hex("#6666FF") : hex("#000000"), ui: esper_statistics("flyfry", [ { type: "toggle", text: "Fliegenbratgerät", topic: "flyfry", - icon: "power" + icon: mdi("power") } ]) }, artnet: { name: "Artnet", position: [535,480], - icon: "spotlight", + icon: mdi("spotlight"), iconColor: ({artnet}) => ({ off: hex("#000000"), @@ -331,7 +358,7 @@ const config : Config = { topic: "artnet", on: "cycle", toggled: val => val != "off", - icon: "power" + icon: mdi("power") }, { type: "dropDown", @@ -345,7 +372,7 @@ const config : Config = { cycle: "Farbwechsel" }, enableCondition: val => val != "off", - icon: "palette" + icon: mdi("palette") } ] }, @@ -354,12 +381,12 @@ const config : Config = { position: [350, 650], iconColor: ({onkyo_connection, onkyo_power}) => onkyo_connection != "connected" ? hex("#888888") : (onkyo_power == "on" ? hex("#00FF00") : hex("#000000")), - icon: "volume-high", + icon: mdi("audio-video"), ui: [ { type: "toggle", text: "Power", - icon: "power", + icon: mdi("power"), topic: "onkyo_power", enableCondition: (a, b, state) => state.onkyo_connection.internal == "connected" }, @@ -373,14 +400,14 @@ const config : Config = { topic: "onkyo_volume", min: 0, max: 50, - icon: "volume-high", + icon: mdi("volume-high"), enableCondition: (a, b, state) => state.onkyo_connection.internal == "connected" }, { type: "toggle", text: "Mute", topic: "onkyo_mute", - icon: "volume-off", + icon: mdi("volume-off"), enableCondition: (a, b, state) => state.onkyo_connection.internal == "connected" }, { @@ -398,7 +425,7 @@ const config : Config = { pult: "Pult", front: "Front HDMI" }, - icon: "usb", + icon: mdi("usb"), enableCondition: (a, b, state) => state.onkyo_connection.internal == "connected" }, { @@ -417,7 +444,7 @@ const config : Config = { somafm_beatblender: "Beat Blender (Soma FM)", ponyville: "Ponyville FM" }, - icon: "radio", + icon: mdi("radio"), enableCondition: (a, b, state) => state.onkyo_connection.internal == "connected" && state.onkyo_inputs.internal == "netzwerk" }, { @@ -434,28 +461,28 @@ const config : Config = { rundumleuchte: { name: "Rundumleuchte", position: [310,275], - icon: "alarm-light", + icon: mdi("alarm-light"), iconColor: ({rundumleuchte}) => rundumleuchte == "on" ? hex("#F0DF10") : hex("#000000"), ui: [ { type: "toggle", text: "Rundumleuchte", topic: "rundumleuchte", - icon: "power" + icon: mdi("power") } ] }, alarm: { name: "Alarm", position: [340, 250], - icon: "alarm-bell", + icon: mdi("alarm-bell"), iconColor: () => hex("#000000"), ui: esper_statistics("alarm") }, door: { name: "Tür", position: [455,350], - icon: "swap-vertical", + icon: mdi("swap-vertical"), iconColor: ({door_status}) => door_status == "on" ? hex("#00FF00") : hex("#FF0000"), ui: [ { @@ -467,21 +494,21 @@ const config : Config = { type: "text", text: "Anwesend", topic: "presence_status", - icon: "account" + icon: mdi("account") } ] }, infoscreen: { name: "Infoscreen", position: [255, 495], - icon: "television-guide flip-v", + icon: mdi("television-guide flip-v"), iconColor: ({infoscreen}) => infoscreen == "on" ? hex("#4444FF") : hex("#000000"), ui: [ { type: "toggle", text: "Infoscreen", topic: "infoscreen", - icon: "power" + icon: mdi("power") }, { type: "link", @@ -493,7 +520,7 @@ const config : Config = { printer_3d: { name: "Ultimaker 3", position: [754, 560], - icon: "printer-3d", + icon: mdi("printer-3d"), iconColor: ({printer_3d_status}) => ({ awaiting_interaction: hex("#b3b300"), @@ -514,7 +541,7 @@ const config : Config = { }, { type: "progress", - icon: "rotate-right", + icon: mdi("rotate-right"), min: 0, max: 1, text: "Printing Progress", @@ -525,7 +552,7 @@ const config : Config = { partkeepr: { name: "Partkeepr", position: [48, 450], - icon: "chip", + icon: mdi("chip"), ui: [ { type: "link", @@ -537,7 +564,7 @@ const config : Config = { kitchen_light: { name: "Deckenlicht Küche", position: [325, 407], - icon: "ceiling-light", + icon: mdi("ceiling-light"), ui: [ { type: "toggle", @@ -546,14 +573,14 @@ const config : Config = { toggled: n => parseInt(n) > 0, topic: "kitchen_light_brightness", text: "Ein/Ausschalten", - icon: "power" + icon: mdi("power") }, { type: "slider", min: 0, max: 100, text: "Helligkeit", - icon: "brightness-7", + icon: mdi("brightness-7"), topic: "kitchen_light_brightness", delayedApply: true }, @@ -562,7 +589,7 @@ const config : Config = { min: 0, max: 100, text: "Farbtemperatur", - icon: "weather-sunset-down", + icon: mdi("weather-sunset-down"), topic: "kitchen_light_color", delayedApply: true }, @@ -575,7 +602,7 @@ const config : Config = { min: 0, max: 100, text: "Helligkeit", - icon: "brightness-7", + icon: mdi("brightness-7"), topic: floalt.brightness("65537"), delayedApply: true }, @@ -584,7 +611,7 @@ const config : Config = { min: 0, max: 100, text: "Farbtemperatur", - icon: "weather-sunset-down", + icon: mdi("weather-sunset-down"), topic: floalt.color("65537"), delayedApply: true }, @@ -597,7 +624,7 @@ const config : Config = { min: 0, max: 100, text: "Helligkeit", - icon: "brightness-7", + icon: mdi("brightness-7"), topic: floalt.brightness("65538"), delayedApply: true }, @@ -606,7 +633,7 @@ const config : Config = { min: 0, max: 100, text: "Farbtemperatur", - icon: "weather-sunset-down", + icon: mdi("weather-sunset-down"), topic: floalt.color("65538"), delayedApply: true }, @@ -619,7 +646,7 @@ const config : Config = { min: 0, max: 100, text: "Helligkeit", - icon: "brightness-7", + icon: mdi("brightness-7"), topic: floalt.brightness("65539"), delayedApply: true }, @@ -628,7 +655,7 @@ const config : Config = { min: 0, max: 100, text: "Farbtemperatur", - icon: "weather-sunset-down", + icon: mdi("weather-sunset-down"), topic: floalt.color("65539"), delayedApply: true }, @@ -641,7 +668,7 @@ const config : Config = { min: 0, max: 100, text: "Helligkeit", - icon: "brightness-7", + icon: mdi("brightness-7"), topic: floalt.brightness("65540"), delayedApply: true }, @@ -650,7 +677,7 @@ const config : Config = { min: 0, max: 100, text: "Farbtemperatur", - icon: "weather-sunset-down", + icon: mdi("weather-sunset-down"), topic: floalt.color("65540"), delayedApply: true } @@ -658,8 +685,8 @@ const config : Config = { }, kitchen_sink_light: { name: "Licht Spüle", - position: [300, 348], - icon: "ceiling-light", + position: [300, 345], + icon: mdi("wall-sconce-flat"), ui: [ { type: "toggle", @@ -668,14 +695,14 @@ const config : Config = { toggled: n => parseInt(n) > 0, topic: "kitchen_sink_light_brightness", text: "Ein/Ausschalten", - icon: "power" + icon: mdi("power") }, { type: "slider", min: 0, max: 100, text: "Helligkeit", - icon: "brightness-7", + icon: mdi("brightness-7"), topic: "kitchen_sink_light_brightness", delayedApply: true } @@ -800,7 +827,8 @@ const config : Config = { { image: require("../img/layers/rzl/labels.svg"), name: "Labels", - defaultVisibility: "visible", + defaultVisibility: "hidden", + opacity: 0.8, bounds: { topLeft: [0, 0], bottomRight: [1000, 700] diff --git a/config/utils.js b/config/utils.js index 3542ca0..e3d4169 100644 --- a/config/utils.js +++ b/config/utils.js @@ -1,5 +1,6 @@ // @flow import type { ControlUI } from "config/flowtypes"; +import { mdi } from "config/icon"; export const esper_topics = (chip_id: string, name: string) => ({ [ `esper_${name}_version` ]: { @@ -88,31 +89,31 @@ export const esper_statistics = (name: string, { type: "text", text: "Device Variant", - icon: "chart-donut", + icon: mdi("chart-donut"), topic: `esper_${name}_device` }, { type: "text", text: "Version", - icon: "source-branch", + icon: mdi("source-branch"), topic: `esper_${name}_version` }, { type: "text", text: "IP", - icon: "access-point-network", + icon: mdi("access-point-network"), topic: `esper_${name}_ip` }, { type: "text", text: "RSSI", - icon: "wifi", + icon: mdi("wifi"), topic: `esper_${name}_rssi` }, { type: "text", text: "Running since…", - icon: "av-timer", + icon: mdi("av-timer"), topic: `esper_${name}_uptime` } ]) diff --git a/css/styles.css b/css/styles.css index 7dcb9af..a695c22 100644 --- a/css/styles.css +++ b/css/styles.css @@ -5,6 +5,7 @@ .leaflet-container { height: calc(100vh - 64px); max-height: 100%; + background: rgba(239,239,203,.59); } body { margin: 0; diff --git a/package.json b/package.json index 8404359..ec41426 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "author": "uwap ", "description": "control devices via mqtt on a beautiful map of your space", "scripts": { - "build": "webpack --bail --config webpack.dev.js --env", - "production-build": "webpack --bail --config webpack.prod.js --env", - "watch": "webpack-dev-server --open --config webpack.dev.js --env", + "build": "webpack --bail --config webpack.config.js -p --env", + "dev": "webpack --bail --config webpack.config.js --mode development --env", + "watch": "webpack-dev-server --open --config webpack.config.js --mode development --env", "travis": "./travis.sh", "lint": "eslint -- --ext js --ext jsx src/", "precommit": "yarn lint" @@ -14,10 +14,9 @@ "dependencies": { "@material-ui/core": "^1.2.1", "@material-ui/lab": "^1.0.0-alpha.5", - "babel-preset-env": "^1.6.0", + "@mdi/font": "^2.0.46", "leaflet": "^1.3.1", "lodash-es": "^4.17.4", - "mdi": "^2.0.46", "mqtt": "^2.14.0", "react": "^16.0.0", "react-dom": "^16.0.0", @@ -31,25 +30,23 @@ "babel-eslint": "^8.0.1", "babel-loader": "^7.1.1", "babel-plugin-transform-class-properties": "^6.24.1", + "babel-preset-env": "^1.6.0", "babel-preset-react": "^6.24.1", "clean-webpack-plugin": "^0.1.18", "css-loader": "^0.28.9", "eslint": "^4.16.0", "eslint-plugin-flowtype": "^2.42.0", "eslint-plugin-react": "^7.6.1", - "extract-text-webpack-plugin": "next", "file-loader": "^1.1.5", "flow": "^0.2.3", - "flow-bin": "^0.70.0", + "flow-bin": "^0.75.0", "flow-typed": "^2.3.0", "html-webpack-plugin": "^3.1.0", "husky": "^0.14.3", - "lodash-webpack-plugin": "^0.11.4", "style-loader": "^0.21.0", "webpack": "^4.3.0", "webpack-cli": "^3.0.0", "webpack-dev-server": "^3.1.1", - "webpack-merge": "^4.1.1", "webpack-shell-plugin": "^0.5.0" }, "license": "MIT" diff --git a/src/components/App.js b/src/components/App.js index fc5becd..7d6fb2f 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -19,7 +19,7 @@ import TopBar from "components/TopBar"; import UiItemList from "components/UiItemList"; import keyOf from "utils/keyOf"; -import { controlGetIcon } from "utils/parseIconName"; +import { toRawIcon } from "config/icon"; import connectMqtt from "../connectMqtt"; @@ -127,7 +127,7 @@ class App extends React.PureComponent { control={this.state.selectedControl} onCloseRequest={this.closeDrawer.bind(this)} icon={this.state.selectedControl == null ? null : - controlGetIcon(this.state.selectedControl, + toRawIcon(this.state.selectedControl.icon, this.state.mqttState)} > {this.state.selectedControl == null diff --git a/src/components/ControlMap.js b/src/components/ControlMap.js index 7524673..2606561 100644 --- a/src/components/ControlMap.js +++ b/src/components/ControlMap.js @@ -4,7 +4,7 @@ import { Map, ImageOverlay, Marker, LayersControl } from "react-leaflet"; import { CRS, point, divIcon } from "leaflet"; import map from "lodash/map"; import mapValues from "lodash/mapValues"; -import parseIconName, { controlGetIcon } from "utils/parseIconName"; +import { toRawIcon } from "config/icon"; import type { Controls, Control } from "config/flowtypes"; @@ -50,8 +50,8 @@ export default class ControlMap extends React.PureComponent { } createLeafletIcon(control: Control) { - const icon = controlGetIcon(control, this.props.state); - const iconClass = parseIconName(`${icon} 36px`); + const icon = toRawIcon(control.icon, this.props.state); + const iconClass = `${icon} mdi-36px`; return divIcon({ iconSize: point(36, 36), iconAnchor: point(18, 18), diff --git a/src/components/SideBar.js b/src/components/SideBar.js index a94e62e..5015bdc 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -8,15 +8,16 @@ import IconButton from "@material-ui/core/IconButton"; import AppBar from "@material-ui/core/AppBar"; import Toolbar from "@material-ui/core/Toolbar"; import List from "@material-ui/core/List"; -import { renderIcon } from "utils/parseIconName"; +import { renderRawIcon } from "config/icon"; +import type { RawIcon } from "config/icon"; import type { Control } from "config/flowtypes"; export type SideBarProps = { control: ?Control, open: boolean, onCloseRequest: () => void, - icon?: ?string, + icon?: ?RawIcon, children?: React.Node }; @@ -55,7 +56,7 @@ class SideBar extends React.PureComponent {this.props.icon == null - || renderIcon(this.props.icon, "mdi-36px")} + || renderRawIcon(this.props.icon, "mdi-36px")} {this.props.control == null || this.props.control.name} diff --git a/src/components/UiItemList/UiItem.js b/src/components/UiItemList/UiItem.js index 8a39b40..886f47c 100644 --- a/src/components/UiItemList/UiItem.js +++ b/src/components/UiItemList/UiItem.js @@ -2,16 +2,15 @@ import React from "react"; import keys from "lodash/keys"; import map from "lodash/map"; -import { - ListItemSecondaryAction, - ListItemText, - ListSubheader -} from "@material-ui/core/List"; +import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction"; +import ListItemText from "@material-ui/core/ListItemText"; +import ListSubheader from "@material-ui/core/ListSubheader"; import Switch from "@material-ui/core/Switch"; -import Input, { InputLabel } from "@material-ui/core/Input"; +import Input from "@material-ui/core/Input"; +import InputLabel from "@material-ui/core/InputLabel"; import FormControl from "@material-ui/core/FormControl"; import Select from "@material-ui/core/Select"; -import { MenuItem } from "@material-ui/core/Menu"; +import MenuItem from "@material-ui/core/MenuItem"; import Button from "@material-ui/core/Button"; import LinearProgress from "@material-ui/core/LinearProgress"; import SliderComponent from "@material-ui/lab/Slider"; @@ -186,7 +185,7 @@ export class DropDown extends UiControl { } export class Slider extends UiControl { - runPrimaryAction = (_e: ?any, v: ?number) => { + runPrimaryAction = (e: ?Event, v: ?number) => { if (v != null) { this.changeState(v); } @@ -194,12 +193,13 @@ export class Slider extends UiControl { render() { return [ - , + , this.props.item.delayedApply || this.runPrimaryAction()} + step={this.props.item.step || 1} + onChange={(e, v) => + this.props.item.delayedApply || this.runPrimaryAction(e, v)} onDragEnd={this.runPrimaryAction} disabled={!this.isEnabled()} /> ]; diff --git a/src/components/UiItemList/index.js b/src/components/UiItemList/index.js index 1014d57..b23b3cc 100644 --- a/src/components/UiItemList/index.js +++ b/src/components/UiItemList/index.js @@ -1,10 +1,8 @@ // @flow import React from "react"; -import { - ListItem, - ListItemIcon -} from "@material-ui/core/List"; -import { renderIcon } from "utils/parseIconName"; +import ListItem from "@material-ui/core/ListItem"; +import ListItemIcon from "@material-ui/core/ListItemIcon"; +import { renderIcon } from "config/icon"; import type { ControlUI } from "config/flowtypes"; @@ -35,7 +33,9 @@ export default class UiItemList extends React.PureComponent { return ( {control.icon == null || - {renderIcon(control.icon, "mdi-24px")}} + + {renderIcon(control.icon, this.props.state, "mdi-24px")} + } {this.renderControl(control)} ); diff --git a/src/config/flowtypes.js b/src/config/flowtypes.js index bef0562..6e58f3b 100644 --- a/src/config/flowtypes.js +++ b/src/config/flowtypes.js @@ -1,5 +1,6 @@ // @flow import type { Color } from "config/colors"; +import type { Icon } from "config/icon"; export type TopicType = (msg: Buffer) => any; @@ -33,7 +34,7 @@ export type UIToggle = $ReadOnly<{| type: "toggle", text: string, topic: string, - icon?: string, + icon?: Icon, enableCondition?: TopicDependentOption, on?: Actual, off?: Actual, @@ -44,7 +45,7 @@ export type UIDropDown = $ReadOnly<{| type: "dropDown", text: string, topic: string, - icon?: string, + icon?: Icon, enableCondition?: TopicDependentOption, options: Map, renderValue?: (value: string) => string @@ -54,7 +55,7 @@ export type UISlider = $ReadOnly<{| type: "slider", text: string, topic: string, - icon?: string, + icon?: Icon, enableCondition?: TopicDependentOption, min?: number, max?: number, @@ -74,21 +75,21 @@ export type UILink = $ReadOnly<{| enableCondition?: StateDependentOption, // TODO: check if both the following options are implemented - icon?: string + icon?: Icon |}>; export type UIText = $ReadOnly<{| type: "text", text: string, topic: string, - icon?: string + icon?: Icon |}>; export type UIProgress = $ReadOnly<{| type: "progress", text: string, topic: string, - icon?: string, + icon?: Icon, min?: number, max?: number |}>; @@ -105,11 +106,7 @@ export type ControlUI = export type Control = { name: string, position: [number, number], - icon: string | ( - internals: Map, - actuals: Map, - state: State - ) => string, + icon: Icon, iconColor?: ( internals: Map, actuals: Map, diff --git a/src/config/icon.js b/src/config/icon.js new file mode 100644 index 0000000..524395e --- /dev/null +++ b/src/config/icon.js @@ -0,0 +1,59 @@ +// @flow +import * as React from "react"; +import { getInternals, getActuals } from "utils/state"; + +export opaque type RawIcon: string = string; + +export type Icon = (Map, Map, State) => + RawIcon; + +export const raw_mdi = (name: string): RawIcon => { + return `mdi ${name.split(" ").map((icon) => "mdi-".concat(icon)).join(" ")}`; +}; + +export const mdi = (icon: string) => () => raw_mdi(icon); + +export const mdi_battery = (topic: string) => + (state: Map) => { + const rawval = state[topic]; + const val = parseInt(rawval); + if (isNaN(val)) { + return raw_mdi("battery-unknown"); + } else if (val > 95) { + return raw_mdi("battery"); + } else if (val > 85) { + return raw_mdi("battery-90"); + } else if (val > 75) { + return raw_mdi("battery-80"); + } else if (val > 65) { + return raw_mdi("battery-70"); + } else if (val > 55) { + return raw_mdi("battery-60"); + } else if (val > 45) { + return raw_mdi("battery-50"); + } else if (val > 35) { + return raw_mdi("battery-40"); + } else if (val > 25) { + return raw_mdi("battery-30"); + } else if (val > 15) { + return raw_mdi("battery-20"); + } else { + return raw_mdi("battery-10"); + } + }; + +export const toRawIcon = (icon: Icon, state: State): RawIcon => { + const internals: Map = getInternals(state); + const actuals: Map = getActuals(state); + return icon(internals, actuals, state); +}; + +export const renderRawIcon = + (icon: RawIcon, extraClass?: string): React.Node => { + return ; + }; + +export const renderIcon = + (icon: Icon, state: State, extraClass?: string): React.Node => { + return renderRawIcon(toRawIcon(icon, state), extraClass); + }; diff --git a/src/index.jsx b/src/index.jsx index 817861c..66997e4 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -5,7 +5,7 @@ import injectTapEventPlugin from "react-tap-event-plugin"; import App from "components/App"; -import "../node_modules/mdi/css/materialdesignicons.min.css"; +import "../node_modules/@mdi/font/css/materialdesignicons.min.css"; import "../css/styles.css"; const Config : Config = window.config; diff --git a/src/utils/parseIconName.js b/src/utils/parseIconName.js deleted file mode 100644 index 96d290f..0000000 --- a/src/utils/parseIconName.js +++ /dev/null @@ -1,25 +0,0 @@ -// @flow -import * as React from "react"; -import { getInternals, getActuals } from "utils/state"; - -import type { Control } from "config/flowtypes"; - -export default function parseIconName(name: string): string { - return `mdi ${name.split(" ").map((icon) => "mdi-".concat(icon)).join(" ")}`; -} - -export const renderIcon = (name: string, extraClass?: string): React.Node => { - return ; -}; - -export const controlGetIcon = (control: Control, state: State): string => { - const internals: Map = getInternals(state); - const actuals: Map = getActuals(state); - return typeof control.icon !== "function" ? control.icon - : control.icon(internals, actuals, state); -}; - -export const renderControlIcon = (control: Control, - state: State, extraClass?: string): React.Node => { - return renderIcon(controlGetIcon(control, state), extraClass); -}; diff --git a/travis.sh b/travis.sh index 1a94da2..d0f1c83 100755 --- a/travis.sh +++ b/travis.sh @@ -5,7 +5,7 @@ for conf in $(ls config/); do if [ "$conf" = "utils.js" ]; then continue fi + yarn dev $conf yarn build $conf - yarn production-build $conf mv dist artifacts/$conf done diff --git a/webpack.common.js b/webpack.config.js similarity index 65% rename from webpack.common.js rename to webpack.config.js index a016dc2..0c38752 100644 --- a/webpack.common.js +++ b/webpack.config.js @@ -2,14 +2,23 @@ const path = require('path'); const webpack = require('webpack'); const WebpackShellPlugin = require('webpack-shell-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); -const ExtractTextPlugin = require("extract-text-webpack-plugin"); const CleanWebpackPlugin = require('clean-webpack-plugin'); - const preBuildScripts = process.env.NO_FLOW == undefined ? process.env.FLOW_PATH != undefined ? [process.env.FLOW_PATH] : ['flow'] : []; -module.exports = { +const configPath = env => { + if (env === true) { + throw "No config file was provided."; + } + return path.resolve(__dirname, `config/${env}`); +}; + +module.exports = env => ({ + entry: { + main: [configPath(env), + path.resolve(__dirname, 'src/index.jsx')] + }, resolve: { modules: [path.resolve(__dirname, "src"), "node_modules"], extensions: ['.js', '.jsx'], @@ -23,7 +32,10 @@ module.exports = { }, module: { rules: [ - { test: /\.(woff2?|eot|ttf|svg)$/, loader: "file-loader" } + // TODO: CSS follow imports and minify + sourcemap on production + { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, + { test: /\.(woff2?|eot|ttf|svg)$/, loader: "file-loader" }, + { test: /\.js(x)?$/, exclude: /node_modules/, loader: "babel-loader?cacheDirectory=true" } ] }, plugins: [ @@ -33,6 +45,5 @@ module.exports = { title: 'Space Map', template: 'index.ejs' }), - new ExtractTextPlugin("styles.css"), ] -}; +}); diff --git a/webpack.dev.js b/webpack.dev.js deleted file mode 100644 index a8135f2..0000000 --- a/webpack.dev.js +++ /dev/null @@ -1,35 +0,0 @@ -const merge = require('webpack-merge'); -const path = require('path'); -const common = require('./webpack.common.js'); -const ExtractTextPlugin = require("extract-text-webpack-plugin"); - -const extractCSS = ExtractTextPlugin.extract({ - fallback: "style-loader", - use: { - loader: "css-loader" - } - }); - -const configPath = env => { - if (env === true) { - throw "No config file was provided."; - } - return path.resolve(__dirname, `config/${env}`); -}; - -module.exports = env => merge(common, { - entry: { - main: [configPath(env), - path.resolve(__dirname, 'src/index.jsx')] - }, - module: { - rules: [ - { test: /\.css$/, use: extractCSS }, - { test: /\.js(x)?$/, exclude: /node_modules/, loader: "babel-loader?cacheDirectory=true" } - ] - }, - devtool: "eval-cheap-module-source-map", - devServer: { - contentBase: './dist' - }, -}); diff --git a/webpack.prod.js b/webpack.prod.js deleted file mode 100644 index 7f4896a..0000000 --- a/webpack.prod.js +++ /dev/null @@ -1,49 +0,0 @@ -const webpack = require('webpack'); -const path = require('path'); -const merge = require('webpack-merge'); -const common = require('./webpack.common.js'); -const ExtractTextPlugin = require("extract-text-webpack-plugin"); -const LodashModuleReplacementPlugin = require('lodash-webpack-plugin'); - -const extractCSS = ExtractTextPlugin.extract({ - fallback: "style-loader", - use: { - loader: "css-loader", - options: { - sourceMap: true, - minimize: true - } - } - }); - -const configPath = env => { - if (env === true) { - throw "No config file was provided."; - } - return path.resolve(__dirname, `config/${env}`); -}; - -module.exports = env => merge(common, { - entry: { - main: [configPath(env), - path.resolve(__dirname, 'src/index.jsx')], - }, - module: { - rules: [ - { test: /\.css$/, use: extractCSS }, - { test: /\.js(x)?$/, exclude: /node_modules/, loader: "babel-loader" } - ] - }, - devtool: "source-map", - plugins: [ - new webpack.DefinePlugin({ - 'process.env': { - 'NODE_ENV': JSON.stringify('production') - } - }), - // new LodashModuleReplacementPlugin(), - new webpack.optimize.OccurrenceOrderPlugin(), - new webpack.HashedModuleIdsPlugin(), - new webpack.optimize.AggressiveMergingPlugin() - ] -}); diff --git a/yarn.lock b/yarn.lock index 948c705..935b0e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -121,6 +121,10 @@ version "1.0.0-alpha.5" resolved "https://registry.yarnpkg.com/@material-ui/lab/-/lab-1.0.0-alpha.5.tgz#2f53a76c4b53aca044c91392ad5e4b47f9a4b241" +"@mdi/font@^2.0.46": + version "2.4.85" + resolved "https://registry.yarnpkg.com/@mdi/font/-/font-2.4.85.tgz#f257a2c3ed1dec3023f2dc12cd9210fab572ab42" + "@types/jss@^9.5.3": version "9.5.3" resolved "https://registry.yarnpkg.com/@types/jss/-/jss-9.5.3.tgz#0c106de3fe0b324cd4173fac7dab26c12cda624e" @@ -171,6 +175,10 @@ acorn@^5.0.0, acorn@^5.5.0: version "5.5.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9" +acorn@^5.3.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" + ajv-keywords@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" @@ -370,12 +378,6 @@ async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" -async@^2.4.1: - version "2.6.0" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4" - dependencies: - lodash "^4.14.0" - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1028,6 +1030,14 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bfj-node4@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/bfj-node4/-/bfj-node4-5.3.1.tgz#e23d8b27057f1d0214fc561142ad9db998f26830" + dependencies: + bluebird "^3.5.1" + check-types "^7.3.0" + tryer "^1.0.0" + big-integer@^1.6.17: version "1.6.27" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.27.tgz#8e56c6f8b2dd6c4fe8d32102b83d4f25868e4b3a" @@ -1382,7 +1392,7 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^2.4.1: +chalk@^2.3.0, chalk@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" dependencies: @@ -1406,6 +1416,10 @@ charenc@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" +check-types@^7.3.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.4.0.tgz#0378ec1b9616ec71f774931a3c6516fad8c152f4" + chokidar@^1.6.1: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" @@ -1590,7 +1604,7 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@2.15.x, commander@^2.11.0, commander@~2.15.0: +commander@2.15.x, commander@^2.11.0, commander@^2.13.0, commander@~2.15.0: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" @@ -2143,6 +2157,10 @@ duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" +duplexer@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + duplexify@^3.4.2, duplexify@^3.5.1, duplexify@^3.5.3: version "3.5.4" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.4.tgz#4bb46c1796eabebeec4ca9a2e66b808cb7a3d8b4" @@ -2162,6 +2180,10 @@ ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" +ejs@^2.5.7: + version "2.6.1" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0" + electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.30: version "1.3.42" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.42.tgz#95c33bf01d0cc405556aec899fe61fd4d76ea0f9" @@ -2500,15 +2522,6 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -extract-text-webpack-plugin@next: - version "4.0.0-beta.0" - resolved "https://registry.yarnpkg.com/extract-text-webpack-plugin/-/extract-text-webpack-plugin-4.0.0-beta.0.tgz#f7361d7ff430b42961f8d1321ba8c1757b5d4c42" - dependencies: - async "^2.4.1" - loader-utils "^1.1.0" - schema-utils "^0.4.5" - webpack-sources "^1.1.0" - extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -2581,6 +2594,10 @@ filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" +filesize@^3.5.11: + version "3.6.1" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" + fill-range@^2.1.0: version "2.2.3" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" @@ -2646,9 +2663,9 @@ flatten@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" -flow-bin@^0.69.0: - version "0.69.0" - resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.69.0.tgz#053159a684a6051fcbf0b71a2eb19a9679082da6" +flow-bin@^0.70.0: + version "0.70.0" + resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.70.0.tgz#080ae83a997f2b4ddb3dc2649bf13336825292b5" flow-typed@^2.3.0: version "2.4.0" @@ -2935,6 +2952,13 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" +gzip-size@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-4.1.0.tgz#8ae096257eabe7d69c45be2b67c448124ffb517c" + dependencies: + duplexer "^0.1.1" + pify "^3.0.0" + handle-thing@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" @@ -3931,12 +3955,6 @@ lodash-es@^4.0.0, lodash-es@^4.17.4, lodash-es@^4.2.1: version "4.17.8" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.8.tgz#6fa8c8c5d337481df0bdf1c0d899d42473121e45" -lodash-webpack-plugin@^0.11.4: - version "0.11.4" - resolved "https://registry.yarnpkg.com/lodash-webpack-plugin/-/lodash-webpack-plugin-0.11.4.tgz#6c3ecba3d4b8d24b53940b63542715c5ed3c4ac5" - dependencies: - lodash "^4.17.4" - lodash.assign@^4.0.3, lodash.assign@^4.0.6: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" @@ -3953,7 +3971,7 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0: +lodash@^4.0.0, lodash@^4.15.0, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0: version "4.17.5" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" @@ -4042,10 +4060,6 @@ md5@^2.1.0: crypt "~0.0.1" is-buffer "~1.1.1" -mdi@^2.0.46: - version "2.2.43" - resolved "https://registry.yarnpkg.com/mdi/-/mdi-2.2.43.tgz#c5e419a6e5f48c82c7109328f52530fd187a0ec0" - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -4514,6 +4528,10 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +opener@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8" + opn@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.3.0.tgz#64871565c863875f052cfdf53d3e3cb5adb53b1c" @@ -6251,6 +6269,10 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" +tryer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.0.tgz#027b69fa823225e551cace3ef03b11f6ab37c1d7" + tslib@^1.9.0: version "1.9.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.2.tgz#8be0cc9a1f6dc7727c38deb16c2ebd1a2892988e" @@ -6561,6 +6583,23 @@ wbuf@^1.1.0, wbuf@^1.7.2: dependencies: minimalistic-assert "^1.0.0" +webpack-bundle-analyzer@^2.13.1: + version "2.13.1" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.13.1.tgz#07d2176c6e86c3cdce4c23e56fae2a7b6b4ad526" + dependencies: + acorn "^5.3.0" + bfj-node4 "^5.2.0" + chalk "^2.3.0" + commander "^2.13.0" + ejs "^2.5.7" + express "^4.16.2" + filesize "^3.5.11" + gzip-size "^4.1.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + opener "^1.4.3" + ws "^4.0.0" + webpack-cli@^3.0.0: version "3.0.8" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.0.8.tgz#90eddcf04a4bfc31aa8c0edc4c76785bc4f1ccd9" @@ -6631,12 +6670,6 @@ webpack-log@^1.0.1, webpack-log@^1.1.2: loglevelnext "^1.0.1" uuid "^3.1.0" -webpack-merge@^4.1.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.2.tgz#5d372dddd3e1e5f8874f5bf5a8e929db09feb216" - dependencies: - lodash "^4.17.5" - webpack-shell-plugin@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/webpack-shell-plugin/-/webpack-shell-plugin-0.5.0.tgz#29b8a1d80ddeae0ddb10e729667f728653c2c742" @@ -6761,6 +6794,13 @@ ws@^3.2.0: safe-buffer "~5.1.0" ultron "~1.1.0" +ws@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-4.1.0.tgz#a979b5d7d4da68bf54efe0408967c324869a7289" + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"