diff --git a/.babelrc b/.babelrc index e0278e3..8b51aa3 100644 --- a/.babelrc +++ b/.babelrc @@ -2,7 +2,9 @@ "presets": [ ["@babel/preset-env", { modules: false, - corejs: 3 + corejs: "3.6", + useBuiltIns: "entry", + targets: "last 3 years" }], "@babel/preset-react", "@babel/preset-flow" diff --git a/README.md b/README.md index bd53203..9eddf0e 100644 --- a/README.md +++ b/README.md @@ -11,33 +11,6 @@ your the mqtt control map for the given CONFIG everytime something changes. for the given config. 4. run `yarn build CONFIG` to generate all files for production use. -## Config +## Documentation -See `config/`. - -The Config format consists out of two sections. Topics and Controls. - -### Topics - -The topics section defines the mqtt interfaces. - -### Controls - -The Controls define the UI Controls. - -| Name | Type | Optional? | Default | Description | -|-----------------|-------------------|------------|-----------------|-------------| -| type | "toggle" \| "dropDown" \| "slider" | No | | The type of the UI element. | -| text | string | No | | The text displayed right next to the UI element. | -| topic | string | No | | The topic the UI element is supposed to change and/or receive its status from. | -| enableCondition | (key: string, value: *) => boolean | Yes | () => true | This option allows you to enable or disable UI elements, depending on the current state. The first parameter is the internal representation of the value. For example "off". The second parameter is the actual value that was received via MQTT. Return true to enable the element and false to disable it. | -| **Toggle Options** | -| on | string | Yes | "on" | If the state is equal to the value of this option the toggle will be toggled on (if the toggled function is not overriden). This is also the value that will be sent when the button is toggled on. | -| off | string | Yes | "off" | If the state is equal to the value of this option the toggle will be toggled off (if the toggled function is not overriden). This is also the value that will be sent when the button is toggled off. | -| toggled | (key: string, value: *) => boolean | Yes | Use the on and off options | This is the function that decides whether the button should be in a toggled state or not. The parameters are equivalent to those of `enableCondition`. Return true to set the button to a toggled state. Return false to set it to the untoggled state. | -| **DropDown Options** | -| options | Map| Yes | {} | This is an attribute set that will map all values defined in the topics section to a description text. For example `{ on: "Lights On", off: "Lights Off" }` will give the drop down element two options, one that is named `Lights On` and one that is named `Lights Off`. | -| **Slider Options** | -| min | number | Yes | 0 | The minimum value of that slider. | -| max | number | Yes | 1 | The maximum value of that slider. | -| step | number | Yes | 1 | The smallest step of the slider. | +The documentation can be found in our [mqtt-control-map wiki](https://github.com/uwap/mqtt-control-map/wiki). diff --git a/config/entropia/index.js b/config/entropia/index.js index c3237a8..593bd97 100644 --- a/config/entropia/index.js +++ b/config/entropia/index.js @@ -1,8 +1,8 @@ // @flow import type { Config } from "config/flowtypes"; -import { hex } from "config/colors"; import * as types from "config/types"; -import { mdi } from "config/icon"; +import * as icons from "@mdi/js"; +import { svg } from "config/icon"; const config: Config = { space: { @@ -32,20 +32,19 @@ const config: Config = { hauptraumTableLight: { name: "Hauptraum Tisch", position: [450, 450], - icon: mdi("white-balance-iridescent"), - iconColor: () => hex("#000000"), + icon: svg(icons.mdiWhiteBalanceIridescent), ui: [ { type: "toggle", text: "Licht", topic: "hauptraumTableLight", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "toggle", text: "Licht", topic: "hauptraumTableLightOnHack", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] } diff --git a/config/rzl/index.js b/config/rzl/index.js index ef8f322..9c2ef78 100644 --- a/config/rzl/index.js +++ b/config/rzl/index.js @@ -2,8 +2,9 @@ import type { Config } from "config/flowtypes"; import * as types from "config/types"; import { hex, rainbow } from "config/colors"; -import { mdi, rawMdi } from "config/icon"; +import { svg, withState } from "config/icon"; import { esper, tasmota } from "./utils"; +import * as icons from "@mdi/js"; import * as onkyo from "./onkyo"; import * as olymp from "./olymp"; @@ -275,29 +276,28 @@ const config: Config = { ledStahltrager: { name: "LED Stahlträger", position: [340, 590], - icon: mdi("white-balance-iridescent"), - iconColor: ({ledStahltraeger}) => - (ledStahltraeger === "on" ? rainbow : hex("#000000")), + icon: svg(icons.mdiWhiteBalanceIridescent).color(({ledStahltraeger}) => + (ledStahltraeger === "on" ? rainbow : hex("#000000"))), ui: [ { type: "toggle", text: "Stahlträger LED", topic: "ledStahltraeger", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, snackbar: { name: "Snackbar", position: [510, 500], - icon: mdi("fridge"), - iconColor: tasmota.iconColor("snackbar", hex("#E20074")), + icon: svg(icons.mdiFridge).color( + tasmota.iconColor("snackbar", hex("#E20074"))), ui: [ { type: "toggle", text: "Snackbar", topic: "snackbar", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "section", @@ -307,7 +307,7 @@ const config: Config = { type: "text", text: "LED-Streifen", topic: "snackbarLedOnline", - icon: mdi("white-balance-iridescent") + icon: svg(icons.mdiWhiteBalanceIridescent) }, { type: "dropDown", @@ -326,7 +326,7 @@ const config: Config = { "11": "Rainbow Pattern", "12": "Fire Pattern" }, - icon: mdi("settings"), + icon: svg(icons.mdiCog), enableCondition: ({ snackbarLedOnline }) => snackbarLedOnline === "on" }, { @@ -335,7 +335,7 @@ const config: Config = { topic: "snackbarDimmmer", min: 0, max: 100, - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), enableCondition: ({ snackbarLedOnline }) => snackbarLedOnline === "on" }, { @@ -344,7 +344,7 @@ const config: Config = { topic: "snackbarSpeed", min: 0, max: 20, - icon: mdi("speedometer"), + icon: svg(icons.mdiSpeedometer), enableCondition: ({ snackbarLedOnline }) => snackbarLedOnline === "on" } ] @@ -352,155 +352,151 @@ const config: Config = { twinkle: { name: "Twinkle", position: [530, 560], - icon: ({twinkle}) => - (twinkle === "on" ? rawMdi("led-on flip-v") : rawMdi("led-off flip-v")), - iconColor: ({twinkle}) => (twinkle === "on" ? rainbow : hex("#000000")), + icon: withState(({twinkle}) => + (twinkle === "on" ? svg(icons.mdiLedOn).flipV().color(rainbow) + : svg(icons.mdiLedOff).flipV()) + ), ui: [ { type: "toggle", text: "Twinkle", topic: "twinkle", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, fan: { name: "Ventilator", position: [530, 440], - icon: mdi("fan"), - iconColor: ({fan}) => (fan === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiFan).color(({fan}) => + (fan === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", text: "Ventilator", topic: "fan", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, cashdesk: { name: "Cashdesk", position: [510, 467], - icon: mdi("coin"), + icon: svg(icons.mdiCurrencyUsdCircle), ui: [ { type: "link", link: "http://cashdesk.rzl:8000/", text: "Open Cashdesk", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) } ] }, flyfry: { name: "Fliegenbratgerät", position: [450, 570], - icon: mdi("fire"), - iconColor: ({flyfry}) => - (flyfry === "on" ? hex("#6666FF") : hex("#000000")), + icon: svg(icons.mdiFire).color(({flyfry}) => + (flyfry === "on" ? hex("#6666FF") : hex("#000000"))), ui: esper.statistics("flyfry", [ { type: "toggle", text: "Fliegenbratgerät", topic: "flyfry", - icon: mdi("power") + icon: svg(icons.mdiPower) } ]) }, projector: { name: "Beamer", position: [380, 590], - icon: mdi("projector flip-v"), - iconColor: ({projector}) => + icon: svg(icons.mdiProjector).flipV().color(({projector}) => ({ transientOn: hex("#b3b300"), transientOff: hex("#b3b300"), on: hex("#00ff00"), off: hex("#000000"), unknown: hex("#888888") - })[projector], + })[projector]), ui: [ { type: "toggle", text: "Beamer", topic: "projector", toggled: (val) => val === "transientOn" || val === "on", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, loetarbeitsplatz4: { name: "Lötarbeitsplatz", position: [205, 455], - icon: mdi("eyedropper-variant"), - iconColor: ({loetarbeitsplatz4}) => - (loetarbeitsplatz4 === "on" ? hex("#FF0000") : hex("#000000")), + icon: svg(icons.mdiEyedropperVariant).color(({loetarbeitsplatz4}) => + (loetarbeitsplatz4 === "on" ? hex("#FF0000") : hex("#000000"))), ui: [ { type: "text", text: "Status", topic: "loetarbeitsplatz4", - icon: mdi("eyedropper-variant") + icon: svg(icons.mdiEyedropperVariant) } ] }, loetarbeitsplatz5: { name: "Lötarbeitsplatz", position: [205, 405], - icon: mdi("eyedropper-variant"), - iconColor: ({loetarbeitsplatz5}) => - (loetarbeitsplatz5 === "on" ? hex("#FF0000") : hex("#000000")), + icon: svg(icons.mdiEyedropperVariant).color(({loetarbeitsplatz4}) => + (loetarbeitsplatz4 === "on" ? hex("#FF0000") : hex("#000000"))), ui: [ { type: "text", text: "Status", topic: "loetarbeitsplatz5", - icon: mdi("eyedropper-variant") + icon: svg(icons.mdiEyedropperVariant) } ] }, door: { name: "Tür", position: [455, 350], - icon: mdi("swap-vertical"), - iconColor: ({doorStatus}) => - (doorStatus === "on" ? hex("#00FF00") : hex("#FF0000")), + icon: svg(icons.mdiSwapVertical).color(({doorStatus}) => + (doorStatus === "on" ? hex("#00FF00") : hex("#FF0000"))), ui: [ { type: "link", link: "http://s.rzl.so", text: "Open Status Page", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) }, { type: "link", // eslint-disable-next-line max-len link: "http://kunterbunt.vm.rzl/dashboard/db/allgemeines-copy-ranlvor?orgId=1", text: "RZL-Dashboard", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) }, { type: "text", text: "Anwesend", topic: "presenceStatus", - icon: mdi("account") + icon: svg(icons.mdiAccount) }, { type: "text", text: "Devices", topic: "devicesStatus", - icon: mdi("wifi") + icon: svg(icons.mdiWifi) }, { type: "toggle", text: "Deko", topic: "deko", - icon: mdi("invert-colors") + icon: svg(icons.mdiInvertColors) }, { type: "text", text: "Power Hauptraum", topic: "powerConsumption", - icon: mdi("speedometer") + icon: svg(icons.mdiSpeedometer) } ] @@ -508,56 +504,56 @@ const config: Config = { infoscreen: { name: "Infoscreen", position: [255, 495], - icon: mdi("television-guide flip-v"), - iconColor: tasmota.iconColor("infoscreen", hex("#4444FF")), + icon: svg(icons.mdiTelevisionGuide).flipV().color( + tasmota.iconColor("infoscreen", hex("#4444FF")) + ), ui: [ { type: "toggle", text: "Infoscreen", topic: "infoscreen", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "link", link: "http://cashdesk.rzl:3030/rzl", text: "Open Infoscreen", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) } ] }, pilze: { name: "Pilze", position: [48, 499], - icon: ({pilze}) => - (pilze === "on" ? rawMdi("led-on") : rawMdi("led-off")), - iconColor: tasmota.iconColor("pilze", rainbow), + icon: withState(({pilze}) => + (pilze === "on" ? svg(icons.mdiLedOn) : svg(icons.mdiLedOff))).color( + tasmota.iconColor("pilze", rainbow)), ui: [ { type: "toggle", text: "Pilze", topic: "pilze", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, printer3D: { name: "Ultimaker 3", position: [754, 560], - icon: mdi("printer-3d"), - iconColor: ({printer3DStatus}) => + icon: svg(icons.mdiPrinter3d).color(({printer3DStatus}) => ({ awaitingInteraction: hex("#b3b300"), printing: hex("#00ff00"), idle: hex("#000000"), unavailable: hex("#888888"), error: hex("#ff0000") - })[printer3DStatus], + })[printer3DStatus]), ui: [ { type: "link", link: "http://ultimaker.rzl/", text: "Open Webinterface", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) }, { type: "section", @@ -565,7 +561,7 @@ const config: Config = { }, { type: "progress", - icon: mdi("rotate-right"), + icon: svg(icons.mdiRotateRight), min: 0, max: 1, text: "Printing Progress", @@ -574,7 +570,7 @@ const config: Config = { { type: "text", text: "Time Left", - icon: mdi("clock"), + icon: svg(icons.mdiClock), topic: "printer3Dremaining" } ] @@ -582,47 +578,45 @@ const config: Config = { partkeepr: { name: "Partkeepr", position: [48, 450], - icon: mdi("chip"), + icon: svg(icons.mdiChip), ui: [ { type: "link", link: "http://partkeepr.rzl/", text: "Open Partkeepr", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) } ] }, printerAnnette: { name: "Drucker", position: [800, 350], - icon: mdi("printer"), - iconColor: tasmota.iconColor("printerAnnette"), + icon: svg(icons.mdiPrinter).color(tasmota.iconColor("printerAnnette")), ui: [ { type: "toggle", text: "Drucker", topic: "printerAnnette", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "link", link: "http://annette.rzl/", text: "Open Annette", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) } ] }, nebenraumPowerStatus: { name: "Strom Fablab", position: [613, 537], - icon: ({nebenraumPowerStatus}) => - (nebenraumPowerStatus === "on" ? rawMdi("flash") : rawMdi("flash-off")), - iconColor: ({nebenraumPowerStatus}) => - (nebenraumPowerStatus === "on" ? hex("#00ff00") : hex("#000000")), + icon: withState(({nebenraumPowerStatus}) => + (nebenraumPowerStatus === "on" ? + svg(icons.mdiFlash).color(hex("#00FF00")) : svg(icons.mdiFlashOff))), ui: [ { type: "text", - icon: mdi("power"), + icon: svg(icons.mdiPower), text: "Strom Fablab", topic: "nebenraumPowerStatus" } @@ -631,19 +625,20 @@ const config: Config = { whirlpool: { name: "Vorstandswhirlpool", position: [1413, 500], - icon: mdi("pool"), - iconColor: ({whirlpoolBubbles}) => - (parseInt(whirlpoolBubbles) > 0 ? hex("#00ff00") : hex("#000000")), + icon: svg(icons.mdiPool).color( + ({whirlpoolBubbles}) => + (parseInt(whirlpoolBubbles, 10) > 0 ? hex("#00ff00") + : hex("#000000"))), ui: [ { type: "text", - icon: mdi("oil-temperature"), + icon: svg(icons.mdiOilTemperature), text: "Temperatur", topic: "whirlpoolTemperature" }, { type: "text", - icon: mdi("oil-temperature"), + icon: svg(icons.mdiOilTemperature), text: "Temperatur Sollwert", topic: "whirlpoolTemperatureSetpoint" }, @@ -652,7 +647,7 @@ const config: Config = { min: 4, max: 100, text: "Temperatur Sollwert", - icon: mdi("oil-temperature"), + icon: svg(icons.mdiOilTemperature), topic: "whirlpoolTemperatureSetpoint" }, { @@ -660,7 +655,7 @@ const config: Config = { min: 0, max: 9, text: "Bubbles", - icon: mdi("chart-bubble"), + icon: svg(icons.mdiChartBubble), topic: "whirlpoolBubbles" } ] diff --git a/config/rzl/kitchen.js b/config/rzl/kitchen.js index a98dee5..89fd23a 100644 --- a/config/rzl/kitchen.js +++ b/config/rzl/kitchen.js @@ -1,9 +1,10 @@ // @flow import type { Topics, Controls } from "config/flowtypes"; -import { mdi, mdiBattery } from "config/icon"; +import { svg, mdiBattery } from "config/icon"; import { hex } from "config/colors"; import * as types from "config/types"; import { floalt, tradfri, tasmota } from "./utils"; +import * as icons from "@mdi/js"; export const topics: Topics = { //Kuechen-Floalts @@ -65,7 +66,7 @@ export const controls: Controls = { kitchenLight: { name: "Deckenlicht Küche", position: [325, 407], - icon: mdi("ceiling-light"), + icon: svg(icons.mdiCeilingLight), ui: [ { type: "toggle", @@ -74,14 +75,14 @@ export const controls: Controls = { toggled: (n) => parseInt(n, 10) > 0, topic: "kitchenLightBrightness", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "slider", min: 0, max: 100, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: "kitchenLightBrightness" }, { @@ -89,7 +90,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Farbtemperatur", - icon: mdi("weather-sunset-down"), + icon: svg(icons.mdiWeatherSunsetDown), topic: "kitchenLightColor" }, { @@ -101,7 +102,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: floalt.brightness("65537") }, { @@ -109,7 +110,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Farbtemperatur", - icon: mdi("weather-sunset-down"), + icon: svg(icons.mdiWeatherSunsetDown), topic: floalt.color("65537") }, { @@ -121,7 +122,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: floalt.brightness("65538") }, { @@ -129,7 +130,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Farbtemperatur", - icon: mdi("weather-sunset-down"), + icon: svg(icons.mdiWeatherSunsetDown), topic: floalt.color("65538") }, { @@ -141,7 +142,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: floalt.brightness("65539") }, { @@ -149,7 +150,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Farbtemperatur", - icon: mdi("weather-sunset-down"), + icon: svg(icons.mdiWeatherSunsetDown), topic: floalt.color("65539") }, { @@ -161,7 +162,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: floalt.brightness("65540") }, { @@ -169,7 +170,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Farbtemperatur", - icon: mdi("weather-sunset-down"), + icon: svg(icons.mdiWeatherSunsetDown), topic: floalt.color("65540") } ] @@ -177,7 +178,7 @@ export const controls: Controls = { kitchenSinkLight: { name: "Licht Spüle", position: [300, 345], - icon: mdi("wall-sconce-flat"), + icon: svg(icons.mdiWallSconceFlat), ui: [ { type: "toggle", @@ -186,14 +187,14 @@ export const controls: Controls = { toggled: (n) => parseInt(n, 10) > 0, topic: "kitchenSinkLightBrightness", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "slider", min: 0, max: 100, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: "kitchenSinkLightBrightness" } ] @@ -201,7 +202,7 @@ export const controls: Controls = { kitchenCounterLight: { name: "Deckenlicht Theke", position: [400, 440], - icon: mdi("ceiling-light"), + icon: svg(icons.mdiCeilingLight), ui: [ { type: "section", @@ -212,7 +213,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: floalt.brightness("65544") }, { @@ -220,7 +221,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Farbtemperatur", - icon: mdi("weather-sunset-down"), + icon: svg(icons.mdiWeatherSunsetDown), topic: floalt.color("65544") }, { @@ -232,7 +233,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: floalt.brightness("65543") }, { @@ -240,7 +241,7 @@ export const controls: Controls = { min: 0, max: 100, text: "Farbtemperatur", - icon: mdi("weather-sunset-down"), + icon: svg(icons.mdiWeatherSunsetDown), topic: floalt.color("65543") } ] @@ -248,21 +249,21 @@ export const controls: Controls = { lichtDunstabzug: { name: "Licht Dunstabzugshaube", position: [252, 405], - icon: mdi("ceiling-light"), + icon: svg(icons.mdiCeilingLight), iconColor: tasmota.iconColor("lichtDunstabzug"), ui: [ { type: "toggle", text: "Licht Dunstabzugshaube", topic: "lichtDunstabzug", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, remotes: { name: "Fernbedinungen", position: [400, 344], - icon: mdi("light-switch"), + icon: svg(icons.mdiLightSwitch), iconColor: (state) => //if any remote is low make icon red (["65536", "65542", "65546", "65547"] .some((x) => state[tradfri.remote.low(x)] === "true") diff --git a/config/rzl/olymp.js b/config/rzl/olymp.js index 02a2d50..8b8e989 100644 --- a/config/rzl/olymp.js +++ b/config/rzl/olymp.js @@ -1,9 +1,10 @@ // @flow import type { Topics, Controls } from "config/flowtypes"; -import { mdi } from "config/icon"; +import { svg } from "config/icon"; import { hex, rainbow } from "config/colors"; import * as types from "config/types"; import { tasmota, esper } from "./utils"; +import * as icons from "@mdi/js"; export const topics: Topics = { ...tasmota.topics("8", "ledOlymp"), @@ -47,66 +48,63 @@ export const controls: Controls = { ledOlymp: { name: "LED Olymp", position: [196, 154], - icon: mdi("white-balance-iridescent rotate-45"), - iconColor: tasmota.iconColor("ledOlymp", rainbow), + icon: svg(icons.mdiWhiteBalanceIridescent).rotate(45).color( + tasmota.iconColor("ledOlymp", rainbow)), ui: [ { type: "toggle", text: "LED Olymp", topic: "ledOlymp", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, videogames: { name: "Videospiele", position: [100, 100], - icon: mdi("gamepad-variant"), - iconColor: ({videogames}) => - (videogames === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiGamepadVariant).color(({videogames}) => + (videogames === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", text: "Videospiele", topic: "videogames", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, olympPC: { name: "Rechner", position: [297, 90], - icon: mdi("desktop-classic"), - iconColor: ({olympPC}) => - (olympPC === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiDesktopClassic).color(({olympPC}) => + (olympPC === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", text: "Rechner", topic: "olympPC", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, rundumleuchte: { name: "Rundumleuchte", position: [310, 275], - icon: mdi("alarm-light"), - iconColor: ({rundumleuchte}) => - (rundumleuchte === "on" ? hex("#F0DF10") : hex("#000000")), + icon: svg(icons.mdiAlarmLight).color(({rundumleuchte}) => + (rundumleuchte === "on" ? hex("#F0DF10") : hex("#000000"))), ui: [ { type: "toggle", text: "Rundumleuchte", topic: "rundumleuchte", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, alarm: { name: "Alarm", position: [340, 250], - icon: mdi("alarm-bell"), + icon: svg(icons.mdiAlarmBell), iconColor: () => hex("#000000"), ui: esper.statistics("alarm") } diff --git a/config/rzl/onkyo.js b/config/rzl/onkyo.js index 7d1e866..d1bca0a 100644 --- a/config/rzl/onkyo.js +++ b/config/rzl/onkyo.js @@ -1,8 +1,9 @@ // @flow import type { Topics, Controls } from "config/flowtypes"; -import { mdi } from "config/icon"; +import { svg } from "config/icon"; import { hex } from "config/colors"; import * as types from "config/types"; +import * as icons from "@mdi/js"; export const topics: Topics = { onkyoConnection: { @@ -132,12 +133,12 @@ export const controls: Controls = { iconColor: ({onkyoConnection, onkyoPower}) => (onkyoConnection !== "connected" ? hex("#888888") : (onkyoPower === "on" ? hex("#00FF00") : hex("#000000"))), - icon: mdi("audio-video"), + icon: svg(icons.mdiAudioVideo), ui: [ { type: "toggle", text: "Power", - icon: mdi("power"), + icon: svg(icons.mdiPower), topic: "onkyoPower", enableCondition: (state) => state.onkyoConnection === "connected" }, @@ -151,14 +152,14 @@ export const controls: Controls = { topic: "onkyoVolume", min: 0, max: 50, - icon: mdi("volume-high"), + icon: svg(icons.mdiVolumeHigh), enableCondition: (state) => state.onkyoConnection === "connected" }, { type: "toggle", text: "Mute", topic: "onkyoMute", - icon: mdi("volume-off"), + icon: svg(icons.mdiVolumeOff), enableCondition: (state) => state.onkyoConnection === "connected" }, { @@ -176,7 +177,7 @@ export const controls: Controls = { pult: "Pult", front: "Front HDMI" }, - icon: mdi("usb"), + icon: svg(icons.mdiUsb), enableCondition: (state) => state.onkyoConnection === "connected" }, { @@ -200,7 +201,7 @@ export const controls: Controls = { somafmChrismasLounge: "Christmas Lounge (SomaFM)", unknown: "Unknown" }, - icon: mdi("radio"), + icon: svg(icons.mdiRadio), enableCondition: (state) => state.onkyoConnection === "connected" && state.onkyoInputs === "netzwerk" }, @@ -212,7 +213,7 @@ export const controls: Controls = { type: "link", link: "http://mpd.rzl/mpd/player/index.php", text: "Open MPD Interface", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) } ] } diff --git a/config/rzl/utils.js b/config/rzl/utils.js index 3ac8e49..5daf02f 100644 --- a/config/rzl/utils.js +++ b/config/rzl/utils.js @@ -1,8 +1,9 @@ // @flow import type { ControlUI, Topics } from "config/flowtypes"; -import { mdi } from "config/icon"; +import { svg } from "config/icon"; import { hex, type Color } from "config/colors"; import * as types from "config/types"; +import * as icons from "@mdi/js"; export const tasmota = { topics: (id: string, name: string): Topics => ({ @@ -106,31 +107,31 @@ const esperStatistics = (name: string, { type: "text", text: "Device Variant", - icon: mdi("chart-donut"), + icon: svg(icons.mdiChartDonut), topic: `esper_${name}_device` }, { type: "text", text: "Version", - icon: mdi("source-branch"), + icon: svg(icons.mdiSourceBranch), topic: `esper_${name}_version` }, { type: "text", text: "IP", - icon: mdi("access-point-network"), + icon: svg(icons.mdiAccessPointNetwork), topic: `esper_${name}_ip` }, { type: "text", text: "RSSI", - icon: mdi("wifi"), + icon: svg(icons.mdiWifi), topic: `esper_${name}_rssi` }, { type: "text", text: "Running since…", - icon: mdi("av-timer"), + icon: svg(icons.mdiAvTimer), topic: `esper_${name}_uptime` } ]) diff --git a/config/uwap-home/index.js b/config/uwap-home/index.js index ac72d07..b4e386b 100644 --- a/config/uwap-home/index.js +++ b/config/uwap-home/index.js @@ -1,8 +1,9 @@ // @flow import type { Config } from "config/flowtypes"; import * as types from "config/types"; -import { mdi, rawMdi } from "config/icon"; +import { svg, withState } from "config/icon"; import { hex } from "config/colors"; +import * as icons from "@mdi/js"; const topicBulbHomeRust = (bulb: string, argument: string) => ({ [`${bulb}${argument}`]: { @@ -100,7 +101,7 @@ const sliderRGB = (bulb: string, argument: string) => ( min: 0, max: 255, text: argument, - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: `${bulb}${argument}` }] ); @@ -110,7 +111,7 @@ const sliderH = (bulb: string, argument: string) => ( min: 0, max: 360, text: argument, - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: `${bulb}${argument}` }] ); @@ -121,7 +122,7 @@ const sliderSVXY = (bulb: string, argument: string) => ( max: 1, step: 0.01, text: argument, - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: `${bulb}${argument}` }] ); @@ -215,36 +216,35 @@ const config: Config = { bedroomLight: { name: "Schlafzimmer", position: [180, 130], - icon: mdi("ceiling-light"), - iconColor: ({bedroomState}) => - (bedroomState === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiCeilingLight).color(({bedroomState}) => + (bedroomState === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", topic: "bedroomState", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "slider", min: 0, max: 255, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: "bedroombrightness" }, { type: "toggle", topic: "bedroomWakeup", text: "Lichtwecker", - icon: mdi("weather-sunset-up") + icon: svg(icons.mdiWeatherSunsetUp) }, { type: "slider", min: 250, max: 454, text: "Farbtemperatur", - icon: mdi("weather-sunset-down"), + icon: svg(icons.mdiWeatherSunsetDown), topic: "bedroomcolor_temp" } ] @@ -252,26 +252,25 @@ const config: Config = { bedroomFan: { name: "Lüftung Schlafzimmer", position: [140, 25], - icon: mdi("fan"), - iconColor: ({fanBedroomState}) => - (fanBedroomState === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiFan).color(({fanBedroomState}) => + (fanBedroomState === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", topic: "fanBedroomState", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "toggle", topic: "fanBedroomAuto", text: "Automatik", - icon: mdi("air-conditioner") + icon: svg(icons.mdiAirConditioner) }, { type: "text", text: "Zieltemperatur", - icon: mdi("temperature-celsius"), + icon: svg(icons.mdiTemperatureCelsius), topic: "fanBedroomTarget" }, { @@ -280,7 +279,7 @@ const config: Config = { max: 25, step: 0.1, text: "Zieltemperatur", - icon: mdi("oil-temperature"), + icon: svg(icons.mdiOilTemperature), topic: "fanBedroomTarget" } ] @@ -288,24 +287,23 @@ const config: Config = { officeSpeaker: { name: "Lautsprecher", position: [245, 658], - icon: ({speakerOfficeState}) => - (speakerOfficeState === "on" ? rawMdi("volume-high") - : rawMdi("volume-off")), - iconColor: ({speakerOfficeState}) => - (speakerOfficeState === "on" ? hex("#00FF00") : hex("#000000")), + icon: withState(({speakerOfficeState}) => + (speakerOfficeState !== "on" ? svg(icons.mdiVolumeOff) + : svg(icons.mdiVolumeHigh).color(hex("#00FF00"))) + ), ui: [ { type: "toggle", topic: "speakerOfficeState", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, officeFan: { name: "Lüftung Büro", position: [140, 658], - icon: mdi("fan"), + icon: svg(icons.mdiFan), iconColor: ({fanOfficeState}) => (fanOfficeState === "on" ? hex("#00FF00") : hex("#000000")), ui: [ @@ -313,18 +311,18 @@ const config: Config = { type: "toggle", topic: "fanOfficeState", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "toggle", topic: "fanOfficeAuto", text: "Automatik", - icon: mdi("air-conditioner") + icon: svg(icons.mdiAirConditioner) }, { type: "text", text: "Zieltemperatur", - icon: mdi("temperature-celsius"), + icon: svg(icons.mdiTemperatureCelsius), topic: "fanOfficeTarget" }, { @@ -333,7 +331,7 @@ const config: Config = { max: 25, step: 0.1, text: "Zieltemperatur", - icon: mdi("oil-temperature"), + icon: svg(icons.mdiOilTemperature), topic: "fanOfficeTarget" } ] @@ -341,25 +339,24 @@ const config: Config = { tucana: { name: "tucana", position: [110, 658], - icon: mdi("desktop-tower"), - iconColor: ({tucanaPower}) => + icon: svg(icons.mdiDesktopTower).color(({tucanaPower}) => ({ "Link Down": hex("#888888"), "1000M": hex("#00ff00"), "10M": hex("#000000") - })[tucanaPower] || hex("#ff0000"), + })[tucanaPower] || hex("#ff0000")), ui: [ { type: "toggle", topic: "tucanaPower", text: "Einschalten", - icon: mdi("power"), + icon: svg(icons.mdiPower), on: "1000M" }, { type: "text", text: "Link Speed", - icon: mdi("ethernet"), + icon: svg(icons.mdiEthernet), topic: "tucanaPower" } ] @@ -367,19 +364,19 @@ const config: Config = { officeSwitch: { name: "Switch Büro", position: [200, 540], - icon: mdi("lan"), + icon: svg(icons.mdiLan), ui: [ { type: "toggle", topic: "officeSwitchPollingActive", text: "Poll switch status", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "link", link: "http://192.168.0.189/", text: "Open Webinterface", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) } ] @@ -387,22 +384,21 @@ const config: Config = { officeLight: { name: "Büro", position: [210, 570], - icon: mdi("ceiling-light"), - iconColor: ({officeState}) => - (officeState === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiCeilingLight).color(({officeState}) => + (officeState === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", topic: "officeState", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "slider", min: 0, max: 255, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: "officebrightness" } ] @@ -410,22 +406,21 @@ const config: Config = { hallwayLight: { name: "Flur", position: [520, 370], - icon: mdi("ceiling-light"), - iconColor: ({hallwayState}) => - (hallwayState === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiCeilingLight).color(({hallwayState}) => + (hallwayState === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", topic: "hallwayState", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "slider", min: 0, max: 255, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: "hallwaybrightness" } ] @@ -433,22 +428,21 @@ const config: Config = { hallway2Light: { name: "Flur", position: [250, 370], - icon: mdi("ceiling-light"), - iconColor: ({hallway2State}) => - (hallway2State === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiCeilingLight).color(({hallway2State}) => + (hallway2State === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", topic: "hallway2State", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "slider", min: 0, max: 255, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: "hallway2brightness" } ] @@ -456,62 +450,60 @@ const config: Config = { pi: { name: "Pi", position: [550, 75], - icon: mdi("raspberry-pi"), + icon: svg(icons.mdiRaspberryPi), ui: [ { type: "toggle", topic: "lueftenHint", text: "Lüften Erinnerung", - icon: mdi("fan") + icon: svg(icons.mdiFan) }, { type: "link", link: "http://192.168.0.12:3000/", text: "Grafana", - icon: mdi("open-in-new") + icon: svg(icons.mdiOpenInNew) } ] }, nas: { name: "NAS", position: [550, 100], - icon: mdi("nas"), - iconColor: ({nasPower}) => - (nasPower === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiNas).color(({nasPower}) => + (nasPower === "on" ? hex("#00FF00") : hex("#000000"))), ui: [ { type: "toggle", topic: "nasPower", text: "Einschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) } ] }, livingroomLight: { name: "Wohnzimmer", position: [450, 200], - icon: mdi("ceiling-light"), - iconColor: ({livingroomState}) => - (livingroomState === "on" ? hex("#00FF00") : hex("#000000")), + icon: svg(icons.mdiCeilingLight).color(({livingroomState}) => + (livingroomState === "on" ? hex("#00FF00") : hex("#000000"))), ui: ([ { type: "toggle", topic: "livingroomState", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "toggle", topic: "livingroomKodiControlled", text: "Kodi Einbindung", - icon: mdi("brightness-auto") + icon: svg(icons.mdiBrightnessAuto) }, { type: "slider", min: 0, max: 255, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: "livingroombrightness" }, { @@ -520,7 +512,7 @@ const config: Config = { min: 300, step: -1, text: "Speed", - icon: mdi("speedometer"), + icon: svg(icons.mdiSpeedometer), topic: "livingroomanimation-speed" }, { @@ -535,7 +527,7 @@ const config: Config = { "3": "RGB Fade", "4": "Work" }, - icon: mdi("settings") + icon: svg(icons.mdiCog) }, { type: "section", @@ -563,7 +555,7 @@ const config: Config = { livingroomLedStrip: { name: "Ledstreifen Wohnzimmer", position: [450, 73], - icon: mdi("white-balance-iridescent"), + icon: svg(icons.mdiWhiteBalanceIridescent), iconColor: ({ledstrip_livingroomState}) => (ledstrip_livingroomState === "on" ? hex("#00FF00") : hex("#000000")), ui: ([ @@ -571,14 +563,14 @@ const config: Config = { type: "toggle", topic: "ledstrip_livingroomState", text: "Ein/Ausschalten", - icon: mdi("power") + icon: svg(icons.mdiPower) }, { type: "slider", min: 0, max: 255, text: "Helligkeit", - icon: mdi("brightness-7"), + icon: svg(icons.mdiBrightness7), topic: "ledstrip_livingroombrightness" }, { @@ -593,7 +585,7 @@ const config: Config = { "3": "RGB Fade", "4": "Work" }, - icon: mdi("settings") + icon: svg(icons.mdiCog) }, { type: "section", diff --git a/index.ejs b/index.ejs index bede683..af4cf0c 100644 --- a/index.ejs +++ b/index.ejs @@ -2,7 +2,6 @@ - <%= htmlWebpackPlugin.options.title %> diff --git a/package.json b/package.json index 5d60251..6403a40 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "author": "uwap ", "description": "Control Devices via mqtt, visualized on a Map", "scripts": { - "build": "webpack --bail --config webpack.config.js -p --env", + "build": "webpack --bail --config webpack.config.js --mode production --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", @@ -13,15 +13,12 @@ }, "dependencies": { "@material-ui/core": "^4.11.0", - "@material-ui/lab": "^4.0.0-alpha.56", - "@mdi/font": "^5.6.55", + "@mdi/react": "^1.4.0", "leaflet": "^1.5.1", - "lodash-es": "^4.17.15", "mqtt": "^4.2.1", "react": "^16.8.6", "react-dom": "^16.8.6", - "react-leaflet": "^2.4.0", - "redux": "^4.0.4" + "react-leaflet": "^2.4.0" }, "devDependencies": { "@babel/cli": "^7.5.5", @@ -31,10 +28,11 @@ "@babel/preset-env": "^7.5.5", "@babel/preset-flow": "^7.0.0-rc.1", "@babel/preset-react": "^7.0.0-rc.1", + "@mdi/js": "^5.6.55", "babel-eslint": "^10.0.2", "babel-loader": "^8.0.6", "clean-webpack-plugin": "^3.0.0", - "core-js": "3", + "core-js": "^3.6.0", "css-loader": "^4.3.0", "eslint": "^7.10.0", "eslint-plugin-flowtype": "^5.2.0", @@ -46,11 +44,12 @@ "flow-typed": "^3.2.1", "html-webpack-plugin": "^4.5.0", "husky": "^4.3.0", + "lodash-es": "^4.17.15", "style-loader": "^1.3.0", - "webpack": "^4.39.1", - "webpack-cli": "^3.3.6", + "webpack": "^4.44.0", + "webpack-cli": "^3.3.0", "webpack-dev-server": "^3.7.2", - "webpack-shell-plugin": "^0.5.0" + "webpack-shell-plugin-next": "^1.2.0" }, "license": "MIT" } diff --git a/src/components/App.js b/src/components/App.js index c6df8d1..818c481 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -39,6 +39,14 @@ export type AppState = { error: ?string }; +/* + *const App = (props: AppProps) => { + * const topics = Array.isArray(props.config.topics) ? + * Object.assign({}, ...props.config.topics) : props.config.topics; + * const [mqttConnected, setMqttConnected] = useState(false); + *}; + */ + class App extends React.PureComponent { controlMap: React.Node @@ -171,7 +179,7 @@ class App extends React.PureComponent { control={this.state.selectedControl} onCloseRequest={this.closeDrawer} icon={this.state.selectedControl == null ? null : - this.state.selectedControl.icon(this.state.mqttState)} + this.state.selectedControl.icon.render(this.state.mqttState)} > {this.state.selectedControl == null || } diff --git a/src/components/ControlMap.js b/src/components/ControlMap.js index 6532002..231dd1b 100644 --- a/src/components/ControlMap.js +++ b/src/components/ControlMap.js @@ -7,6 +7,7 @@ import filter from "lodash/filter"; import reduce from "lodash/reduce"; import MqttContext from "mqtt/context"; import type { Controls, Control, UIControl, ControlUI } from "config/flowtypes"; +import { renderToString } from "react-dom/server"; export type Point = [number, number]; @@ -28,21 +29,11 @@ const center = (props: ControlMapProps): Point => props.height / 2 ]); -const iconColor = (control: Control, state: State): string => { - if (control.iconColor != null) { - return control.iconColor(state); - } - return "#000"; -}; - const createLeafletIcon = (control: Control, state: State) => { - const icon = control.icon(state); - const iconClass = `${icon} mdi-36px`; return divIcon({ iconSize: point(36, 36), iconAnchor: point(18, 18), - html: `` + html: renderToString(control.icon.render(state)) }); }; diff --git a/src/components/SideBar.js b/src/components/SideBar.js index 2d4499c..c4a3cce 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -8,16 +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 { renderRawIcon } from "config/icon"; +import ReactIcon from "@mdi/react"; +import { mdiClose } from "@mdi/js"; -import type { RawIcon } from "config/icon"; import type { Control } from "config/flowtypes"; export type SideBarProps = { control: ?Control, open: boolean, onCloseRequest: () => void, - icon?: ?RawIcon, + icon?: ?React.Node, children?: React.Node }; @@ -43,13 +43,13 @@ const SideBar = (props: SideBarProps) => { - {props.icon == null || renderRawIcon(props.icon, "mdi-36px")} + {props.icon == null || props.icon} {props.control == null ? "" : props.control.name} - + diff --git a/src/components/TopBar.js b/src/components/TopBar.js index 8f9055e..cd935c6 100644 --- a/src/components/TopBar.js +++ b/src/components/TopBar.js @@ -9,6 +9,8 @@ import { fade } from "@material-ui/core/styles/colorManipulator"; import { makeStyles } from "@material-ui/core/styles"; import Tooltip from "@material-ui/core/Tooltip"; import IconButton from "@material-ui/core/IconButton"; +import ReactIcon from "@mdi/react"; +import { mdiMap, mdiGithub } from "@mdi/js"; export type TopBarProps = { connected: boolean, @@ -21,7 +23,7 @@ export type SearchBarProps = { const renderConnectionIndicator = (connected: boolean) => { if (connected) { - return (); + return (); } return ( @@ -89,22 +91,15 @@ const Search = (props: SearchBarProps) => { const openOnGithub = () => window.open( "https://github.com/uwap/mqtt-control-map", "_blank"); -const sendFeedback = () => window.open("mailto:mail+feedback@uwap.name"); - const TopBar = (props: TopBarProps) => ( {renderConnectionIndicator(props.connected)} - - - - - - - - + + + diff --git a/src/components/UiItems/Link.js b/src/components/UiItems/Link.js index 506f86d..5592e85 100644 --- a/src/components/UiItems/Link.js +++ b/src/components/UiItems/Link.js @@ -2,7 +2,6 @@ import React from "react"; import createComponent from "./base"; import { isEnabled, isDisabled } from "./utils"; -import { renderRawIcon } from "config/icon"; import type { UILink } from "config/flowtypes"; @@ -19,7 +18,7 @@ const Icon = ({item, state}) => { if (item.icon == null) { return false; } - return renderRawIcon(item.icon(state), "mdi-24px"); + return item.icon.render(state); }; const BaseComponent = (_h, item: UILink, state, _changeState) => ( diff --git a/src/components/UiItems/base.js b/src/components/UiItems/base.js index dd359a6..4e55ffc 100644 --- a/src/components/UiItems/base.js +++ b/src/components/UiItems/base.js @@ -7,13 +7,12 @@ import ListItemText from "@material-ui/core/ListItemText"; import ListItemIcon from "@material-ui/core/ListItemIcon"; import throttle from "lodash/throttle"; -import { renderRawIcon } from "config/icon"; import type { Icon } from "config/icon"; export type Helpers = { - Icon: (props: Object) => React.Node, - Label: (props: Object) => React.Node, - Action: (props: Object) => React.Node + Icon: (props: { item: { +icon?: Icon }, state: State }) => React.Node, + Label: (props: {}) => React.Node, + Action: (props: {}) => React.Node }; export type BaseComponent = ( @@ -41,15 +40,15 @@ type SuperT = $ReadOnly<{ text: string }>; const IconHelper = ({item, state}: { item: { +icon?: Icon }, state: State }) => ( - {item.icon == null || renderRawIcon(item.icon(state), "mdi-24px")} + {item.icon == null || item.icon.size(1).render(state)} ); const createHelpers = (item: T) => ({ Icon: IconHelper, - Label: (props) => ( - + Label: () => ( + ), Action: (props) => ( diff --git a/src/components/UiItems/index.js b/src/components/UiItems/index.js index 8c8e44f..875fd60 100644 --- a/src/components/UiItems/index.js +++ b/src/components/UiItems/index.js @@ -6,10 +6,11 @@ import Link from "./Link"; import Slider from "./Slider"; import Text from "./Text"; import Progress from "./Progress"; +import * as React from "react"; import type { ControlUI } from "config/flowtypes"; -const Control = ({item}: {item: ControlUI}) => { +const Control = ({item}: {item: ControlUI}): React.Node => { switch (item.type) { case "toggle": { return Toggle.component(item); diff --git a/src/config/flowtypes.js b/src/config/flowtypes.js index f3fe9d3..be2091e 100644 --- a/src/config/flowtypes.js +++ b/src/config/flowtypes.js @@ -1,5 +1,4 @@ // @flow -import type { Color } from "config/colors"; import type { Icon } from "config/icon"; export type TopicType = (msg: Buffer) => string; @@ -100,7 +99,6 @@ export type Control = { name: string, position: [number, number], icon: Icon, - iconColor?: (state: State) => Color, ui: Array }; export type Controls = Map; diff --git a/src/config/icon.js b/src/config/icon.js index a8a44b1..54a598b 100644 --- a/src/config/icon.js +++ b/src/config/icon.js @@ -1,55 +1,88 @@ // @flow -import * as React from "react"; -import ReactContext from "mqtt/context"; +import React from "react"; +import ReactIcon from "@mdi/react"; +import { type Color } from "./colors"; +import * as mdiIcons from "@mdi/js"; -export opaque type RawIcon: string = string; - -export type Icon = (State) => RawIcon; - -export const rawMdi = (name: string): RawIcon => { - return `mdi ${name.split(" ").map((icon) => "mdi-".concat(icon)).join(" ")}`; +type IconPropHelper = { + size?: number, + rotate?: number, + horizontal?: boolean, + vertical?: boolean, + color?: Color }; -export const mdi = (icon: string) => () => rawMdi(icon); +export type Icon = { + render: (s: State) => React.Node, + size: (n: number) => Icon, + rotate: (n: number) => Icon, + flip: () => Icon, + flipV: () => Icon, + color: (c: Color | (State) => Color) => Icon, + applyProps: (props: IconPropHelper) => Icon +}; -export const mdiBattery = (topic: string) => (state: State) => { +const iconChainUtils = (cb: (x: T, p?: IconPropHelper) => Icon, + p1: T, p?: IconPropHelper) => ({ + size: (n: number) => cb(p1, {...p, size: n}), + rotate: (n: number) => cb(p1, {...p, rotate: n}), + flip: () => cb(p1, {...p, horizontal: !p?.horizontal ?? true}), + flipV: () => cb(p1, {...p, vertical: !p?.vertical ?? true}), + color: (c: Color | (State) => Color) => cb(p1, {...p, color: c}), + applyProps: (props: IconPropHelper) => cb(p1, {...p, ...props}) + } + ); + +export const svg = (data: string, props?: IconPropHelper): Icon => { + const propColor = ((c: Color | (State) => Color) => (state: State) => { + if (typeof c === "function") { + return c(state); + } + return c; + })(props?.color ?? "black"); + return { + render: (state) => ( + + ), + ...iconChainUtils(svg, data, props) + }; +}; + +export const withState = (f: (s: State) => Icon, + props?: IconPropHelper): Icon => ({ + render: (state) => f(state).applyProps(props).render(state), + ...iconChainUtils(withState, f, props) +} +); + +export const mdiBattery = (topic: string): Icon => withState((state) => { const rawval = state[topic]; const val = parseInt(rawval, 10); if (isNaN(val)) { - return rawMdi("battery-unknown"); + return svg(mdiIcons.mdiBatteryUnknown); } else if (val > 95) { - return rawMdi("battery"); + return svg(mdiIcons.mdiBattery); } else if (val > 85) { - return rawMdi("battery-90"); + return svg(mdiIcons.mdiBattery90); } else if (val > 75) { - return rawMdi("battery-80"); + return svg(mdiIcons.mdiBattery80); } else if (val > 65) { - return rawMdi("battery-70"); + return svg(mdiIcons.mdiBattery70); } else if (val > 55) { - return rawMdi("battery-60"); + return svg(mdiIcons.mdiBattery60); } else if (val > 45) { - return rawMdi("battery-50"); + return svg(mdiIcons.mdiBattery50); } else if (val > 35) { - return rawMdi("battery-40"); + return svg(mdiIcons.mdiBattery40); } else if (val > 25) { - return rawMdi("battery-30"); + return svg(mdiIcons.mdiBattery30); } else if (val > 15) { - return rawMdi("battery-20"); - } else { - return rawMdi("battery-10"); + return svg(mdiIcons.mdiBattery20); } -}; - -export const renderRawIcon = - (icon: RawIcon, extraClass?: string): React.Node => { - return ; - }; - -export const renderIcon = - (icon: Icon, extraClass?: string): React.Node => { - return ( - - {({state}) => renderRawIcon(icon(state), extraClass)} - - ); - }; + return svg(mdiIcons.mdiBattery10); +}); diff --git a/src/config/types.js b/src/config/types.js index 6867807..478108d 100644 --- a/src/config/types.js +++ b/src/config/types.js @@ -17,7 +17,7 @@ export const option = (values: TypeOptionParam): TopicType => { return values.otherwise; } else { throw new Error( - `Value ${x.toString()} cannot by mapped by the option parameters given` + `Value ${x.toString()} cannot be mapped by the option parameters given` ); } }; diff --git a/src/connectMqtt.js b/src/connectMqtt.js index 0085f28..ee1af34 100644 --- a/src/connectMqtt.js +++ b/src/connectMqtt.js @@ -49,7 +49,7 @@ export default function connectMqtt( } }); return (topic: string, message: Buffer) => { - client.publish(topic, message, null, (error) => { + client.publish(topic, message, {}, (error) => { if (error == null && settings.onMessageSent != null) { settings.onMessageSent(topic, message); } diff --git a/src/index.jsx b/src/index.jsx index 392f8e4..60400b8 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -1,10 +1,13 @@ // @flow +import "core-js/stable"; +import "regenerator-runtime/runtime"; +import "../node_modules/leaflet/dist/leaflet.css" + import React from "react"; import ReactDOM from "react-dom"; import App from "components/App"; -import "../node_modules/@mdi/font/css/materialdesignicons.min.css"; import "../css/styles.css"; import type { Config } from "config/flowtypes"; diff --git a/src/mqtt/context.js b/src/mqtt/context.js index bb14c9c..1f47c9f 100644 --- a/src/mqtt/context.js +++ b/src/mqtt/context.js @@ -3,10 +3,10 @@ import React from "react"; export type MqttContextValue = { state: State, - changeState: (topic: string, value: string) => void + changeState: (topic: string, value: string) => State }; export default React.createContext({ state: {}, - changeState: (_topic, _val) => {} + changeState: (_topic, _val) => ({}) }); diff --git a/webpack.config.js b/webpack.config.js index 31501b3..89d6f9a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,6 +1,6 @@ const path = require('path'); const webpack = require('webpack'); -const WebpackShellPlugin = require('webpack-shell-plugin'); +const WebpackShellPlugin = require('webpack-shell-plugin-next'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const preBuildScripts = process.env.NO_FLOW == undefined ? @@ -16,14 +16,15 @@ const configPath = env => { module.exports = env => ({ entry: { - main: ["core-js/stable", "regenerator-runtime/runtime", configPath(env), + main: [configPath(env), path.resolve(__dirname, 'src/index.jsx')] }, resolve: { modules: [path.resolve(__dirname, "src"), "node_modules"], extensions: ['.js', '.jsx'], alias: { - 'lodash': 'lodash-es' + 'lodash': 'lodash-es', + "leaflet": "leaflet/dist/leaflet-src.esm.js" } }, output: { @@ -34,8 +35,8 @@ module.exports = env => ({ rules: [ // TODO: CSS follow imports and minify + sourcemap on production { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, - { test: /\.(woff2?|eot|ttf|svg)$/, use: [ { loader: "file-loader", options: { esModule: false } } ] }, - { test: /\.js(x)?$/, loader: "babel-loader?cacheDirectory=true" } + { test: /\.(woff2?|eot|ttf|svg|png)$/, use: [ { loader: "file-loader", options: { esModule: false } } ] }, + { test: /\.js(x)?$/, use: ["babel-loader?cacheDirectory=true"] } ] }, plugins: [ @@ -44,6 +45,6 @@ module.exports = env => ({ new HtmlWebpackPlugin({ title: 'Space Map', template: 'index.ejs' - }), + }) ] }); diff --git a/yarn.lock b/yarn.lock index 4fe972b..ba267e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -993,17 +993,6 @@ react-is "^16.8.0" react-transition-group "^4.4.0" -"@material-ui/lab@^4.0.0-alpha.56": - version "4.0.0-alpha.56" - resolved "https://registry.yarnpkg.com/@material-ui/lab/-/lab-4.0.0-alpha.56.tgz#ff63080949b55b40625e056bbda05e130d216d34" - integrity sha512-xPlkK+z/6y/24ka4gVJgwPfoCF4RCh8dXb1BNE7MtF9bXEBLN/lBxNTK8VAa0qm3V2oinA6xtUIdcRh0aeRtVw== - dependencies: - "@babel/runtime" "^7.4.4" - "@material-ui/utils" "^4.10.2" - clsx "^1.0.4" - prop-types "^15.7.2" - react-is "^16.8.0" - "@material-ui/styles@^4.10.0": version "4.10.0" resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.10.0.tgz#2406dc23aa358217aa8cc772e6237bd7f0544071" @@ -1050,10 +1039,15 @@ prop-types "^15.7.2" react-is "^16.8.0" -"@mdi/font@^5.6.55": +"@mdi/js@^5.6.55": version "5.6.55" - resolved "https://registry.yarnpkg.com/@mdi/font/-/font-5.6.55.tgz#3536d7a7671eda9d5d9ba191b9fb9aceccc0a07e" - integrity sha512-6wWrpTXiv2wBtoCL+EJ9Xxfy6Tv6Q1KxmrX54/M23tBNmdGmh417y1tn327oXQxO1nq7BiHGAKkWQZ/GQPLTCA== + resolved "https://registry.yarnpkg.com/@mdi/js/-/js-5.6.55.tgz#d1e99da22c8d462c17d4c5b530a7d1b77e668230" + integrity sha512-FphEd6PTivVSfsxjRB/Z5sJhxUmxA0jL/+nEQmqk8Ok1miSnw92ibj4p1SPAmgnR8OFTNCkHX23AvlU8YZQb5A== + +"@mdi/react@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@mdi/react/-/react-1.4.0.tgz#7f6bad1bd8801226d0e5bad91659b215ce68a93f" + integrity sha512-OUH9RhfDJPhybQL3owwrSDIXz2yVKXg5lYeOZjyRCiT9wqywNK0FeYyDByOwNIZnnIQoQYmuSrMv+pOX0Uqkmw== "@octokit/auth-token@^2.4.0": version "2.4.2" @@ -1470,9 +1464,9 @@ acorn-jsx@^5.2.0: integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== acorn@^6.4.1: - version "6.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" - integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== acorn@^7.4.0: version "7.4.0" @@ -2431,7 +2425,7 @@ core-js-compat@^3.6.2: browserslist "^4.8.5" semver "7.0.0" -core-js@3: +core-js@^3.6.0: version "3.6.5" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== @@ -5973,14 +5967,6 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" -redux@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" - integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== - dependencies: - loose-envify "^1.4.0" - symbol-observable "^1.2.0" - regenerate-unicode-properties@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" @@ -6752,11 +6738,6 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -symbol-observable@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" - integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== - table@^5.2.3, table@^5.4.6: version "5.4.6" resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" @@ -7202,7 +7183,7 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -webpack-cli@^3.3.6: +webpack-cli@^3.3.0: version "3.3.12" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a" integrity sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag== @@ -7277,10 +7258,10 @@ webpack-log@^2.0.0: ansi-colors "^3.0.0" uuid "^3.3.2" -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" - integrity sha1-Kbih2A3erg3bEOcpZn9yhlPCx0I= +webpack-shell-plugin-next@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/webpack-shell-plugin-next/-/webpack-shell-plugin-next-1.2.0.tgz#6a62931110c08ab36d2f0c225648a63331b7bbc2" + integrity sha512-jBLDs436jbKWnLZCqeN4hr7abXx0Tnz6Iwx7ejxh1h4JSmsnc82kGOBC63U6RKl0F6bvaXFsUOiIKV+DFKNxNw== webpack-sources@^1.4.0, webpack-sources@^1.4.1: version "1.4.3" @@ -7290,7 +7271,7 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1: source-list-map "^2.0.0" source-map "~0.6.1" -webpack@^4.39.1: +webpack@^4.44.0: version "4.44.2" resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.2.tgz#6bfe2b0af055c8b2d1e90ed2cd9363f841266b72" integrity sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==