uwap / mqtt-control-map

Highly configurable MQTT map interface
6 stars 3 forks source link

New config format #7

Open uwap opened 6 years ago

uwap commented 6 years ago

We should design a new config format which is cleaner.

Open questions:

Bfritz0815 commented 6 years ago

Is there a list, what issues exist with the existing format?

Am 27.10.2017 22:25 schrieb "uwap" notifications@github.com:

We should design a new config format which is cleaner

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/uwap/space-map/issues/7, or mute the thread https://github.com/notifications/unsubscribe-auth/AAvaC0EhTmA1Lp4EcKIHTfDZKQML6mW9ks5swjw0gaJpZM4QJkwK .

uwap commented 6 years ago

it is clumsy. especially it will get more clumsy when I change a bit about the ui part. I guess I should first make the changes before thinking about how to clean up the config format.

uwap commented 6 years ago

We should definely change

my_component = {
  icon: "home",
  iconColor: ({some_topic}) => some_topic == "some_value" ? "#000" : "#111"
}

to

my_component = {
  icon: {
    name: "home",
    color: ({some_topic}) => some_topic == "some_value" ? "#000" : "#111"
  }
}

or even to

my_component = {
  icon: {
    mdi-name: "home",
    color: ({some_topic}) => some_topic == "some_value" ? "#000" : "#111"
  }
}

A further idea would be to also add a <component>.icon.topic option for a more consistent behavior of functions in the config. That would lead to something like:

my_component = {
  icon: {
    mdi-name: "home",
    color: (value) => value == "some_value" ? "#000" : "#111",
    topic: "some_topic"
  }
}

it would also allow to mix different icon sets:

first_component = {
  icon: {
    mdi-name: "home" // material design icons
  }
},
second_component = {
  icon: {
    fa-name: "bell" // font awesome icons
  }
}

Edit: Maybe do it like this:

first_component = {
  icon: {
    name: mdi("home") // material design icons
  }
},
second_component = {
  icon: {
    name: font-awesome("bell") // font awesome icons
  }
}
uwap commented 6 years ago

Additionally to the icons being weird in the config format, I think that the topics section is weird too.

I think it is not great that parseState is needed that often. For the esper support in utils I needed to make the topics section an array. It is now an array of javascript objects, that gets merged. This is totally weird and provides possible error sources.

First of all, I want to provide a new way to describe message types. So instead of

onkyo_mute: {
        state: "/service/onkyo/status/audio-muting",
        command: "/service/onkyo/command",
        defaultValue: "AMT00",
        values: { off: "AMT00", on: "AMT01" },
        parseState: msg => JSON.parse(msg.toString()).onkyo_raw
}

I want it to be like:

onkyo_mute: {
        state: "/service/onkyo/status/audio-muting",
        command: "/service/onkyo/command",
        defaultValue: "AMT00",
        values: { off: "AMT00", on: "AMT01" },
        type: json("onkyo_raw")
}

Possible types would be string, number, json and date.

For the array part I need to brainstorm a bit more about possible ideas. cc @Bfritz0815 @Ranlvor

uwap commented 6 years ago

I like to rewrite icon colors.

Right now it is always a function. I don't like it. Instead, I want it to be either a color newtype or a javascript object or a function.

Examples:

icon: {
  color: hex("#333333")
}
icon: {
  color: rgb(255, 255, 100)
}
icon: {
  topic: "lightswitch",
  color: {
    on: hex("#333999"),
    off: rgba(255, 100, 100, 0.2)
  }
}
icon: {
  topic: "lightswitch",
  color: {
    on: hex("#333999"),
    off: rgba(255, 100, 100, 0.2)
    default: hex("#FFFFFF")
  }
}

Maybe default should be always required or maybe the map should do a check if all possible values are covered.

Last but not least only for complex setups it should be possible to use a function:

icon: {
  color: ({lightswitch}) => lightswitch == "on" ? hex("#333999") : hex("#001122")
}
uwap commented 6 years ago

Maybe flows $Keys type could be used to force the use of all values for colors if default is not given:

uwap commented 6 years ago

We should make the type option for a topic required and not only have it define parseState but also let it define values (and maybe defaultValue too?).

This could result in this for example:

my_toggle: {
        state: "/service/toggle/status",
        command: "/service/toggle/command",
        type: types.enum({on: "ON", off: "OFF"})
}
uwap commented 6 years ago

I always thought the internal representation of values was kind of redundant and unintuitive but sadly I couldn't find a way around that. Now that I'm looking at the topic types again I really think I've found a way.

Originally the parseState function was only a way around the lack of representations in the config format. But we could actually use it to emulate the internal representation thing. The best example is the my_toggle topic from above. We could have types like types.enum that actually map a value onto a representation. This could also work the other way around.

The only problem with this representation now is that parseState only worked on the state and the internal representation work on both and we still want to support having different types in both directions.

uwap commented 6 years ago

I also thought about the types.json example. A json could contain all sorts of values. Therefore I thought about making the following types valid

type: types.json("volume", types.number)
type: types.json("name", types.string)
type: types.json("switchState", types.enum({on: "POWERED", off: "DOWN"}))
uwap commented 6 years ago

In the very beginning of this project, we had actions. Each UI control would trigger an action. The action itself would then do something on certain topics. This seemed redundant to me, so we removed it really quickly. Especially since it had some flaws. A slider, mutating values, and a toggle button could in no way share the same actions. But the way we defined actions had no way of preventing it.

We need to reinvent actions in a non redundant way. One slider for example could easily modify multiple values, for example when changing the brightness of a lighting group. A toggle button could toggle multiple things at once. It should be even possible to have it toggle between two of many options.

uwap commented 6 years ago

One way I can think of this is expanding the topic setting to actual allow multiple topics.

For example an old way of defining a button could look like this:

{
          type: "toggle",
          on: "50",
          off: "0",
          toggled: n => parseInt(n) > 0,
          topic: "kitchen_light_brightness",
          text: "Ein/Ausschalten",
          icon: mdi("power")
}

A new way could look like this:

{
          type: "toggle",
          topics: {
            kitchen_light_brightness: {
              on: "50",
              off: "0",
              toggled: n => parseInt(n) > 0
            },
            kitchen_light_color: {
              on: "50",
              off: "0",
              toggled: true
            }
          },
          text: "Ein/Ausschalten",
          icon: mdi("power")
},

This already has multiple drawbacks:

uwap commented 6 years ago

The slider is a more interesting topic though. If a slider changes a light group consisting out of 3 light bulbs, each with their own mqtt topic, which one sets the actual light brightness / the state for this component?

uwap commented 6 years ago

Another thing that should be possible is having a slider controlling the light for example. When the slider is turned down to 0 is should be able to send "OFF", when it is turned higher than 0 it should sent "ON", but only once. I want the new actions to capable of doing so.

uwap commented 6 years ago

I thought about it. Maybe we can do something like this:

{
          type: "slider",
          topic: "kitchen_light_brightness",
          text: "Brightness",
          icon: mdi("power"),
          actions: [
            onDrag.oldValueEquals(0).newValueAbove(0).trigger("kitchen_light_power", "on"),
            onDrag.oldValueAbove(0).newValueEquals(0).trigger("kitchen_light_power", "off")
          ]
}

This, but with a more elegant API would be a valid way to describe actions, I think.

uwap commented 3 years ago

With the config being essential close to JSON, the syntax seems increadibly bloaty. While actions might be a solution to increase the complexity of what we can express and might have the change to express this in a short and eloquent manner, it would be nice to have a custom syntax.

@ Define Config
[Space]
name: "Entropia"
color: "orange"
mqtt: "ws://...:1884"

[Topics]
"hauptraumTableLight" defaults to: "off"
- command topic "/public/sensoren/TPH/leinwand/control" as option("A1 ON" -> "on", "A1 OFF" -> "off")

"hauptraumTableLightOnHack" defaults to: "on"
- command topic "/public/sensoren/TPH/leinwand/control" as option("A1 ON" -> "on", "A1 OFF" -> "off")

[Controls]
Define "Hauptraum Tisch" for "hauptraumTableLight" at [450, 450]:
- icon: mdi("white-balance-iridescent"
- iconColor: hex("#000000")
With "Licht" as "toggle":
- topic: "hauptraumTableLight"
- icon: mdi("power")
With "Licht" as "toggle":
- topic: "hauptraumTableLightOnHack"
- icon: mdi("power")

// Define ... for ... etc.

[Layers]
Define BaseLayer "Entropia" from "./assets/layers/rooms.svg" as default "visible":
- opacity: 0.7
With Bounds:
- topLeft: [0, 0]
- bottomRight: [720, 680]

@ End Config

I could imagine something like this instead of the current entropia config. There might be a way to parse custom javascript syntax using Babel. We could then transform it to javascript. The only issue here would be the flow type checker, as it's important that it runs through AFTER transforming the custom syntax into javascript.