Floor plan suggestions

That is incorrect since it should be values[0].brightness_avmain. But you don't really want that I don't think. Also you may wish to go back and re-read the VueJS docs about how/whether/when deep variables are recognised as being updated. If you do use an object variable in data, make sure that you initialise all of the possible properties otherwise you may need to force Vue to recognise that a property has changed.

Actually, I don't think there are :face_with_monocle:

But I do think that you have jumped into the deep end and you are hitting some edge-cases in Vue. As with any framework/library, you have to obey its rules - even the unwritten or not so obvious ones. Vue is better than most at avoiding these things (as is uibuilder :wink:) but there are a few things that will catch you out. I am far from being a Vue expert.

If it weren't hard, they wouldn't be paying you the big bucks ... oh, hang on! :rofl:

@TotallyInformation

Sometimes you have to know when to give up.

I have been reading all night about things like declaring reactive properties and I have tried it all e.g. using the $set method instead of what I was doing originally which apparently does not create a reactive property.

Where this all started
I didn't want to hardcode all my light names etc and write my code in a way that I have to hardcode test each light individually ie. repeating the same code over and over again, however in the end I may have built 50% more if I'd just done that. haha!!!

So I created variables for each light and for each key value I needed to store. I then wrote my code to work off the variables, however I hit a stumbling block.

I needed to lookup a string = the variable name e.g. this string "brightness_avmain" sits in another variable. I then needed to test the value of the variable = brightness_avmain. This is why I tried to use eval() in the first instance, but it looks like you can only use eval() on the right hand side of the equals sign.... ie I can't get eval() to work and when I read up more on it I keep getting scared away from using it.... seems like it's bad voodoo.

Then I moved to name/value pairs thinking very cleverly that I had sorted it all out, but then came the reactive issues. I came up with a hack which I'm almost wishing I'd explored more which was to update one of the other key variables each time I did any variable updates... this kind of seemed to work, but I stopped as it felt like a nasty hack.

So back to all that reading up tonight. Including this website amongst many:https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties

I tried everything I came across including $set, but could not get anything to fully function

app1.$set(app1.values, app1.values[device_lookup.colourname], msg.colour)

Basically I just haven't been able to figure out how to make a key/value or name/value (depending on where you come from) pair array, like the one below, reactive ie make it so it updates the web page straight away

      values: [{brightness_avmain:"10",
                 brightness_avspots:"11",
                 brightness_skylight:"12",
                 colour_skylight:"#000000"},
                ],

BTW: I tried curly brackets around each name/value and just one pair of curly brackets around the lot and I can't see any difference. I'm sure there is one, but it ain't working any differently at the front end.

So after fight and losing, in the end I gave up, and hacked it... haha. Yes, I didn't want to hack it to start with, but I've given in and hacked it!!!... I think this hack is better anyway :smiley:

So, I have gone back to my boring old variables e.g.

        brightness_avmain:"10",
        brightness_avspots:"11",
        brightness_skylight:"12",
        colour_skylight:"#000000",

BUT I have also kept the name/value pair array above. I now update both, why might you ask?
This way I can reference the boring old variables in my HTML (which works i.e. the values update correctly the first time) and I can also test the value of a string, which I can do with an array that is a name/value pair....

I'm sure there is a better way, but I've burnt many hours now to get where I am and at the end of the day it works!!!

Onto the next task. woo hoo!!!!

All I need now is some clever cloggs to come in and say, it's really easy you only have to do x, y and z.... Don't do it. I don't want to know. I have to move on... haha!

Hi Alex,

man .. i was reading your posts last night and i was speechless.
I didnt know what to reply. All i could do is it dig in and start reading your code.

  1. I have a question. What was the purpose of 'values' array ? You created it because devices was not reactive and you defined it as an array, which like 'devices' it itself was not reactive ? double trouble :wink:

  2. sliderChange and colourChange send a msg to Node-red immediately on change of value. I would suggest adding a v-button (Apply) and only sending a msg to NR when user is done setting up his/her changes of a device. This way you avoid fat finders messing up :wink:

  3. You have to redefine and keep the device msg object consistent. Both in properties for all devices and for msgs coming from NR. for example. 'brightness' should be brightness_value both for Vue app and NR. So it'll be easier during coding.

  4. you already know that there is a lifecycle hook called mounted for vue. On mounted you'll send a message to NR to reply to Vue what the current status and values of the devices are ? hows that gonna work ?

index.js

"use strict";

// Define the VueJS app
var app1 = new Vue({
  el: "#app",
  data: {
    input_circuit: "no selection",
    input_brightness: 0,
    input_colour: "#000000",
    input_hc2name: "no selection",
    input_localname: "no selection",
    input_brightnessname: "no selection",
    input_brightness_value: 0,
    input_rgbw: false,

    devices: [
      {
        hc2name: "Loft AV/AV Main 1P",
        localname: "avmain",
        brightnessname: "brightness_avmain",
        brightness_value: 0,
        rgbw: false,
      },
      {
        hc2name: "Loft AV/AV Spots 2P",
        localname: "avspots",
        brightnessname: "brightness_avspots",
        brightness_value: 0,
        rgbw: false,
      },
      {
        hc2name: "Corridors/Loft Sky RGB 6P",
        localname: "skylight",
        brightnessname: "brightness_skylight",
        brightness_value: 0,
        rgbw: true,
        colourname: "colour_skylight",
      },
    ],

    // values: [
    //   { brightness_avmain: "0" },
    //   { brightness_avspots: "0" },
    //   { brightness_skylight: "0" },
    //   { colour_skylight: "#000000" },
    // ],
  }, // --- End of data --- //

  methods: {
    // -- Called if a LIGHT BULB is clicked
    clickLight: function(localname) {
      console.log("Somebody clicked the " + localname);
      //var device_lookup = app1.devices;      // no need just use app1
      var device_lookup = {};
      var device_lookup_index = null;
      device_lookup = app1.devices.find((dev) => dev.localname == localname); // returns object with device that matches localname
      device_lookup_index = app1.devices.findIndex(
        (dev) => dev.localname == localname
      ); // we will need index too to use in voodoo $set
      console.log("device_lookup", device_lookup);
      console.log("device_lookup_index", device_lookup_index);

      //this.input_circuit = device_lookup.hc2name;
      //this.input_brightness = app1.values[device_lookup.brightnessname];
      if (device_lookup.rgbw === true) {
        app1.input_colour = device_lookup.colourname; // device_lookup updates input_colour for easy reactive html
      }
    },

    // -- Lighting groups -- //
    clickLightGroup: function(group_name) {
      this.input_circuit = group_name;
    },

    // Called if slider value has been changed
    sliderChange: function(input_brightness) {
      // Send Updated BRIGHTNESS to Node Red //
      uibuilder.send({
        topic: this.input_circuit,
        payload: Number(input_brightness),
        brightness: Number(input_brightness),
      });
    }, // -- End of  -- //

    colourChange: function(input_colour) {
      // Send COLOUR to Node Red //
      uibuilder.send({
        topic: this.input_circuit,
        payload: input_colour,
        colour: input_colour,
        type: "colour",
      });
    }, // -- End of  -- //

    changeElement: function(localname, status, colour) {
      var el = document.getElementById(localname);
      let light_colour = colour === null ? "orange" : colour;
      if (status === true) {
        el.style.color = light_colour;
        el.style.fontSize = "1.5em";
        el.style.textShadow = "0px 0px 14px #fff700";
      } else {
        el.style.color = "grey";
        el.style.fontSize = "1em";
        el.style.textShadow = "0px 0px 0px #fff700";
      }
    },
  }, // --- End of methods --- //

  // Start-up
  mounted: function() {
    /** **REQUIRED** Start uibuilder comms with Node-RED */
    uibuilder.start();

    // msg is updated when a standard msg is received from Node-RED
    uibuilder.onChange("msg", (msg) => {
      console.log("Message received from Node-red", msg)
      // prepare device specific variables
      var device_name = msg.topic;
      //var device_lookup = app1.devices;
      var device_lookup = {}; // you are using lookup again .. lookup should be seperate method .. not to be repedative
      var device_lookup_index = null;
      device_lookup = app1.devices.find(dev => dev.hc2name == device_name);
      device_lookup_index = app1.devices.findIndex(dev => dev.hc2name == device_name);
      console.log("device_lookup", device_lookup);
      console.log("device_lookup_index", device_lookup_index);
      // SPOT LIGHTS //
      // check to see that the msg.topic is in the table of devices and the device is not and rgbw light
      if (device_lookup !== undefined && device_lookup.rgbw === false) {
        if (msg.payload.brightness_value !== undefined) {
          // app1.values[device_lookup.brightnessname] = msg.brightness;
          // console.log(
          //   "msg.bright 2= ",
          //   app1.values[device_lookup.brightnessname] +
          //     " - " +
          //     device_lookup.brightnessname
          // );

          // time for some voodoo
          console.log("Brightness Value from NR : ",msg.payload.brightness_value)
          device_lookup.brightness_value = msg.payload.brightness_value;   // ???????? what is the structure of msg ???
          app1.devices[device_lookup_index].brightness_value = msg.payload.brightness_value;
         // app1.$set(app1.devices, device_lookup_index, device_lookup)   // replace device array at index i with updated device_lookup  

          if (msg.brightness > 0) {
           // app1.changeElement(device_lookup.localname, true, "orange");
          } else {
           // app1.changeElement(device_lookup.localname, false);
          }
        }
      }

      //LIGHTING CONTROL - USER INPUT //
      // Updates the lighting control values on incoming message to keep them syncs if a user makes a change outside of this web page
      device_name = msg.topic;
      device_name = device_name.substring(device_name.search("/") + 1);

      if (device_name == app1.input_circuit) {
        if (msg.brightness !== undefined) {
          app1.input_brightness = msg.brightness;
        }
        if (msg.colour !== undefined) {
          app1.input_colour = msg.colour;
        }
      }

      //Incoming msg log
      //console.log('incoming msg', app1.isonavmain1p, app1.isonavspots1, msg)
    });
  }, // --- End of mounted hook --- //
}); // --- End of app1 --- //

/*
            // RGBW LIGHTS // -- should merge with spot lights!
            // check to see that the msg.topic is in the table of devices and the device is an rgbw light
            console.log("up to here 1")
            if (device_lookup  !== undefined  && device_lookup.rgbw === true)
            {   console.log("up to here 2")
                if (msg.colour !== undefined ) 
                {
                    var status = ""
                    app1.values[device_lookup.colourname] = msg.colour
                    if (app1.values[device_lookup.brightnessname] > 0) {status = true} else {status = false}
                        app1.changeElement(device_lookup.localname,status,msg.colour)
                }
                if (msg.brightness !== undefined  ) 
                {
                    app1.values[device_lookup.brightnessname] = msg.brightness
                    if (msg.brightness > 0) {app1.changeElement(device_lookup.localname,true,app1.values[device_lookup.colourname] )} 
                    else                    {app1.changeElement(device_lookup.localname,false)}
                }
            }


*/

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta
      name="viewport"
      content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes"
    />

    <title>Node-RED UI Builder: SVG Test</title>
    <meta
      name="description"
      content="Node-RED UI Builder - SVG Test with VueJS + bootstrap-vue"
    />

    <link rel="icon" href="./images/node-blue.ico" />

    <link
      type="text/css"
      rel="stylesheet"
      href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css"
    />
    <link
      type="text/css"
      rel="stylesheet"
      href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css"
    />

    <link rel="stylesheet" href="./index.css" media="all" />
  </head>
  <body>
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
    />

    <!-- This is where the output from the Vue #app is displayed -->
    <b-container id="app" style="color: white">
      <h1 style="text-align: center">
        SW6 Floor Plan
      </h1>

      <div
        id="floorplan"
        style="position:relative; left: -400px; width: 1900px; height:42em; background:url(./images/groundfloor.PNG);"
      >
        <!-- RGBW LEDS -->
        <div style="position:absolute; left:480px;  top:264px">
          <i id="skylight" class="fa fa-circle" aria-hidden="false"> </i>
        </div>
        <div style="position:absolute; left:472px;  top:264px">
          <button
            class="btn btn-primary-outline"
            v-on:click="clickLight('skylight')"
            v-b-popover.focus.top="'Skylight RGBW'"
          ></button>
        </div>
        <div
          style="position:absolute; left:500px;  top:264px; font-size: 90%; color: #383737"
        >
          {{devices[2].brightness_value}}
        </div>

        <!-- LED SPOT LIGHTS -->
        <div style="position:absolute; left:580px;  top:264px">
          <i id="avmain" class="fa fa-circle" aria-hidden="false"> </i>
        </div>
        <div style="position:absolute; left:572px;  top:264px">
          <button
            class="btn btn-primary-outline"
            v-on:click="clickLight('avmain')"
            v-b-popover.focus.top="'AV Main'"
          ></button>
        </div>
        <div
          style="position:absolute; left:600px;  top:264px; font-size: 90%; color: #383737"
        >
        {{devices[0].brightness_value}}
        </div>

        <div style="position:absolute; left:680px;  top:264px">
          <i id="avspots" class="fa fa-circle" aria-hidden="false"> </i>
        </div>
        <div style="position:absolute; left:672px;  top:264px">
          <button
            class="btn btn-primary-outline"
            v-on:click="clickLight('avspots')"
            v-b-popover.focus.top="'AV Spot2'"
          ></button>
        </div>
        <div
          style="position:absolute; left:700px;  top:264px; font-size: 90%; color: #383737"
        >
        {{devices[1].brightness_value}}
        </div>
      </div>

      <template>
        <div style="position:relative; width:200px; top:20px; left:20px;">
          <label for="range-10">Circuit: {{ input_circuit }} </label>
          <b-form-input
            lazy
            v-on:change="sliderChange(input_brightness)"
            id="range-10"
            v-model="input_brightness"
            type="range"
            min="0"
            max="99"
          ></b-form-input>
          <div class="mt-2">Brightness: {{ input_brightness }}</div>
        </div>

        <div style="position:relative; width:200px; top:60px; left:20px;">
          <input
            @change="colourChange(input_colour)"
            v-model="input_colour"
            type="color"
          />
          <div class="mt-2">Colour: {{ input_colour }}</div>
        </div>

        <div style="position:relative; width:50px; top:-110px; left:-100px;">
          <button v-on:click="clickLightGroup('Group Kitchen RGBW')">
            All down lights
          </button>
        </div>
      </template>
    </b-container>

    <!-- These MUST be in the right order. Note no leading / -->
    <!-- REQUIRED: Socket.IO is loaded only once for all instances
    Without this, you don't get a websocket connection -->
    <script src="../uibuilder/vendor/socket.io/socket.io.js"></script>

    <!-- Vendor Libraries - Load in the right order -->
    <script src="../uibuilder/vendor/vue/dist/vue.js"></script>
    <!-- dev version with component compiler -->
    <!-- <script src="../uibuilder/vendor/vue/dist/vue.min.js"></script>   prod version with component compiler -->
    <!-- <script src="../uibuilder/vendor/vue/dist/vue.runtime.min.js"></script>   prod version without component compiler -->
    <script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js"></script>

    <!-- REQUIRED: Sets up Socket listeners and the msg object -->
    <!-- <script src="./uibuilderfe.js"></script>   //dev version -->
    <script src="./uibuilderfe.min.js"></script>
    <!--    //prod version -->
    <!-- OPTIONAL: You probably want this. Put your custom code here -->
    <script src="./index.js"></script>
  </body>
</html>

Sure. Ofcourse. Maybe some parts need to better written for WIKI.
Parcel is real easy. It just reads the js files and downloads all related import resources automatically. Now i dont know much about what happens in the background but it works.

Im sending you a link to the src files i use that also has an example with vue router
https://we.tl/t-GEpQukmsnH

ps. Wetransfer keeps the files for up to a week.

1 Like

Correct, it's a workaround.

I'll check that out for another purpose, however the process is you select a light and then you change the brightness and colour in another section of the screen, so at that point in time it's ok to move the slider around and play with the light settings in quick succession i.e. it's ok to make changes at that stage and in fact i'd rather be able to increase/decrease the lights relatively quickly.

I think you are talking about good coding practice here, but I'm not entirely clear what you mean.
eg. I have a variable that stores the brightness for each light e.g. brightness_avmain.


  //set to 101 to see if anything upstream has broken
        brightness_avmain:"101",
        brightness_avspots:"101",
        brightness_skylight:"101",
        colour_skylight:"#000000",

        //set to 101 to see if anything upstream has broken
        values: [{brightness_avmain:"101",
                 brightness_avspots:"101",
                 brightness_skylight:"101",
                 colour_skylight:"#000000"},
                ],

I have called the msg coming into uibuilder for brightness mgs.brightness. Not sure there's much more to it. I have kept the link to msg.brightness and msg.colour by calling the light specific variables brightness_x and colour_x.

Ok, I have to go now. Have to spend time with the family. I'll re-read what you have written on this point, but as of now I'm not clear on it. The code works fine as it is. When node-red starts or all flows are restarted my HA system feeds the latest status of each light. When a web page is refreshed it pulls all the brightness/colour data correctly from @TotallyInformation's cache nodes, which I re-worked to deal with 2x messages e.g. msg.brightness and msg.colour.Later on I'll be adding more messages to the cache node for global variables and sonos stuff. All this I have tested and it works, but I'll read up more on mounted and see if I get what you mean.

There is a whole bunch of stuff that you can't see outside of the uibuilder node that maybe is making it unclear to you how it works, but it does work. Very well in fact.

Thanks for your input!

Its unnecessary to have 'values' when you already have all the information you need concentrated in 'devices' array.

regarding its reactivity, me too i wasnt sure, or stambled on this with my limited experience with Vue but for an example try this :

somewhere on your html add {{devices[2].brightness_value}} which is the skylight?!
(and have the skylight information in devices array that you had commented out)

and from browser console run for example ..

app1.devices[2].brightness_value = 33;

does it update on the dom ?

regardless ... excellent work :wink:

Just realised I didn't answer your question. I need to test the brightness of each light in order to determine the css formatting ie on /off, but I don't want to hardcode these tests so I created a key /value variable for which I know how to pull the value string and test it like it is a variable.

I hope that makes sense.

Edit : you can see this in my js file. It's the 2x functions (from memory I think they are valued colourChange and brightnessChange) that are called from the onchange function which runs each time a new message is received by node red.

What CSS property would that be ? Opacity ?

consider this console command : app1.devices[2].opacity = 0.3; ( range 0.0 - 1.0 )

devices: [
      {
        hc2name: "Loft AV/AV Main 1P",
        localname: "avmain",
        brightnessname: "brightness_avmain",
        brightness_value: 0,
        rgbw: false,
      },
      {
        hc2name: "Loft AV/AV Spots 2P",
        localname: "avspots",
        brightnessname: "brightness_avspots",
        brightness_value: 0,
        rgbw: false,
      },
      {
        hc2name: "Corridors/Loft Sky RGB 6P",
        localname: "skylight",
        brightnessname: "brightness_skylight",
        brightness_value: 0,
        opacity: 0.7,   //////////// added for css  ///////////
        rgbw: true,
        colourname: "colour_skylight",
      },
    ],

and in html :

<div style="position:absolute; left:480px;  top:264px" v-bind:style="{opacity: devices[2].opacity}">
<i id="skylight" class="fa fa-circle" aria-hidden="false"> </i>
</div>

I wouldnt mind hardcoding some configuration in, if it was in one place.
Sometimes its unavoidable .. like positioning those lights with css :smile:

ps. now that i was writing this i just realized that because the devices are in a fixed length array (hardcoded) thats why possibly now they are reactive .. there in no arr.push() .pop() involved ..

I'll look at your posts properly tonight but my focus now is getting the floor plan built rather than optimising it.

Thanks for your comments. I have lots lot to read up on.

I've been building the sidebar and header today for my floorplan, well actually it's intended to be the header and sidebar for EVERYTHING going forward, however I've hit a mental block..... I can link to my Node-Red dashboard pages, but then of course when I click on a link the dashboard page will be loaded with it's own header and sidebar.

I don't want that. I want my new uibuilder header and sidebar to be the consistent view for all my flows..... have I dreamt too big???

This is what I have built, by no means complete (obs)...

  1. Header with 3x menu bar:

  2. Menu bar open, but missing links (I pasted the pic just to give you an idea really)

It looks similar to the default Node-RED dashboard menu bar and header e.g.
image

But when I add a href to my uibuilder sidebar:

<p>An absolute URL: <a href="http://192.168.1.71:1880/ui/#!/1">W3Schools</a></p>

It ends up loading the dashboard page, obviously.

I'm missing something obvious here...

How to link my existing dashboards to my new uibuilder interface (header and sidebar), but keep the uibuilder interface consistent across everything... that is the question.

PS: I think it's possible to emulate the default Node-RED dashboard header and sidebar quite easily. If a noob like me can do it in a few hours, then someone who actually knows what they are doing could blow it out the park.
I already have some ideas on how to make it even better the the dashboard header and sidebar.... just my opinion of course.

Have you tried using iframes like suggested a couple of days ago.
Its the only way i can think of unless someone has a better idea.

<iframe v-bind:src="iframe_url" style="border:none;" title="Iframe Example"></iframe>

you can v-bind the src tag of iframe to a vue data iframe_url = '' variable

every time you click a button in uibuilder dashboard populate that iframe_url = '' with the destination src .ie "/ui/#!/1"

ofcourse you have to implement hidding the iframe with vue condition v-if when you dont need it and show the uibuilder html and vice versa
Vue Conditionals

its kinda messy and i havent tested any of this but you can experiment with this idea.

Why have you used an array there?

If I were doing this, I would start with my data structure:

...
data: {
  lights: {},
},
...

Then I would set up each light based on wherever you are getting the definitions from. I would assume that your lights are all defined somewhere in Node-RED? If so, you need to send a startup msg (which you need to cache so that new connections or refreshed pages get the data). Here is an example of a msg that might send this:

{
    topic: "light_definitions",
    payload: {
        livingroom1: {x: 10, y:50, brightness:10, colour:"#000000"},
        skylight: {x:20, y:100, brightness:12, colour:"#000000"},
        ....
    }
}

Where the brightness and colour values would be the defaults. Note that I've also sent through the x & y values that you would want so that you can position your lights on your background image. I'd probably set these manually in my master data though later you could get clever and use drag/drop processing on the front end to let you add/remove/move lights. Given a small number of lights though, it isn't worth bothering with that except as an exercise in improving your coding skills.

In my front-end code, inside the onchange method, I would check for the topic so I knew what msg type I was handling and then populate the lights data variable using $set - I think that you would have to loop through the keys of the incoming payload and use set to create each element of the lights object but I'm not sure, I'd have to check.

Once you have loaded the defaults and pre-populated all of the possible lights, further updates would simply need to send the data for specific light (if controlling from Node-RED).

Controlling from the front-end, you would firstly have a loop through the keys of the lights variable in your HTML to add each light to the page. Once you've done that and assuming that you've added a control UI to each light, you can now easily send updates back to Node-RED using a msg structure something like:

{
    topic: "skylight",
    payload: {
        brightness:24, colour:"#101010"
    }
}

You could then update your master variable (using Node-RED's retained variables maybe) so that restarting Node-RED would keep and reuse the current setting if that's what you want. Then you would also send the appropriate command to the light of course.

@UnborN

I recall reading this and then i forgot about it. I'll at it to the list of things to do. Thanks!
In the meantime, maybe someone has a better idea.

@TotallyInformation

Err, I probably have a logic error somewhere in my code, but I explained above why I did what i did. I don't want to repeat it all again.
I think you have defined an object. Maybe if I'd started with an object it would have worked.

Everything else you have explained I have working already, except for the drag and drop stuff. Now that would be nice, as I am about to spend too much time positioning things the hard way. Something I was trying to avoid by using variable to only have to enter the x (left) & y (top) position once.

Instead now I'm entering 3x sets of data for each light, but hey, it works:

            <div style="position:absolute; left:1280px;  top:264px" >  <i id="kitlounge" class="fa fa-circle" aria-hidden="false"></i></div>
            <div style="position:absolute; left:1272px;  top:264px" >  <button class="btn btn-primary-outline" v-on:click="clickLight('kitlounge')" v-b-popover.focus.top="'Kitchen Lounge 2x'"></button></div>
            <div style="position:absolute; left:1304px;  top:266px;"    class="light_brightness" > {{brightness_kitlounge}} </div>

Thanks for the feedback

It's a school night so I am going to have to call it, but I wanted to share progress thus far.

I am starting to get something the resembles the final product now.....

With all the lights off:

When you select and RGB/RGBW light the colour picker and some shortcuts appear:

I really don't like how the colour picker and shortcuts look. It reminds me of Windows 3.1.
In the end I'll think I'll just get a compiler and import me a nice big fat colour wheel that looks slick.

As you'd expect from the comment above, my focus in the short term is to look at the visuals and then I have accrued quite a log of functionality to add, but for now she works!!!

Thank you guys for your support, esp @TotallyInformation and @UnborN!!!

3 Likes

Keep 'devices' as an array of objects. You need it to be an array so you'll be able to loop through it.
and so that fancy code you have written to find() 'device_lookup' will work :wink:

But indeed the structure of the device object needs to be re-viewed as Julian also noted.

Adding those x y values in each individual device is a nice touch. I was thinking about that also but i didn't want to bombard you with extra information :wink: This with help you setting the position dynamically as you intended.

Its looking good Alex. Its shaping up :+1:

[EDIT]

I've been testing this issue with the bootstrap-vue popover you were having. The problem is that you were using the v-b-popover.focus.top="'Kitchen Lounge 2x'" focus trigger and the element you were using it on cant actually have a focus. So if you use the following in html it can cleanup a lot of the mess with css positioning. vbind:style .left and .right to dynamically position the device can also be used.

<!-- AV Main -->
            <div
              class="device"
              v-b-popover.click.blur.top="'AV Main'" tabindex="0"
              v-bind:style="{opacity: devices[0].opacity, left: devices[0].left, top: devices[0].top }"
              v-on:click="clickLight('avmain')"
            >
              <i id="avmain" class="fa fa-circle"></i>
              <div>{{devices[0].brightness_value}}</div>
            </div>

ok, so I have no idea why I was reluctant to add a build step, but now that I am in the process of doing it the benefits seem far greater than the cost.

I have installed parcel and created my 3x core files and I can get basic stuff to work e.g. in http://localhost:1234/, I can get some text on the screen... woo hoo!

So, a questions, that hopefully I answer myself before the night is out:

  1. Can you see your whole uibuilder project from http://localhost:1234/ when it's configured correctly?
    EDIT: So far it looks like this does work, but I haven't migrated all my code yet though, so not sure if it will when I need node-red data that it will work.

  2. What else did you do to port from @TotallyInformation's base html file over to parcel?

I'm talking about all this code:

   <!-- These MUST be in the right order. Note no leading / -->
    <!-- REQUIRED: Socket.IO is loaded only once for all instances
    Without this, you don't get a websocket connection -->
    <script src="../uibuilder/vendor/socket.io/socket.io.js"></script>

    <!-- Vendor Libraries - Load in the right order -->
    <script src="../uibuilder/vendor/vue/dist/vue.js"></script> <!-- dev version with component compiler -->
    <!-- <script src="../uibuilder/vendor/vue/dist/vue.min.js"></script>   prod version with component compiler -->
    <!-- <script src="../uibuilder/vendor/vue/dist/vue.runtime.min.js"></script>   prod version without component compiler -->
    <script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js"></script>
    <script src="../uibuilder/vendor/animated-vue\dist/animated-vue.js"></script>
    


    <!-- REQUIRED: Sets up Socket listeners and the msg object -->
    <!-- <script src="./uibuilderfe.js"></script>   //dev version -->
    <script src="./uibuilderfe.min.js"></script> <!--    //prod version -->
    <!-- OPTIONAL: You probably want this. Put your custom code here -->
    <script src="./index.js"></script>

And the code at the top:

<!doctype html><html lang="en"><head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">

    <title>Node-RED UI Builder: SVG Test</title>
    <meta name="description" content="Node-RED UI Builder - SVG Test with VueJS + bootstrap-vue">

    <link rel="icon" href="./images/node-blue.ico">

    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css" />
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css" />
    
    <link rel="stylesheet" href="./index.css" media="all">

There are many benefits .. the only downside is that you have to re-build for every little change.

Im sending you my src files to study what i have in each file .. which includes an example of how i used vue-router ..

Download link https://we.tl/t-2T359iEqsb (wetransfer keeps the files for a week )

copy the files in your new src preferably for a new ' uibuilder node with path 'test
and from that src folder run the parcel command to automatically build in 'dist'

parcel build index.html --public-url ./ --no-cache --out-dir ../dist/ --no-source-maps

since you are on windows .. you can create a batch file on desktop parcelBuild.bat

cd C:\Users\User\.node-red\uibuilder\test\src
cls
cmd /k parcel build index.html --public-url ./  --no-cache --out-dir ../dist/ --no-source-maps

ps .. replace the cd with your path

1 Like

Awesome!

Do you have to do this every time you restart the Node-RED servier?

I ask this because I have a half working model i.e. I can see part of my floorplan in http://localhost:1234/ and the exact same thing in my uibuilder url: http://192.168.1.XX:1880/floorplan/

But as soon as I restarted Node-RED the uibuilder part disappeared.

EDIT: I answered my own question. You have to run this code every time you restart node-red:

parcel build index.html --public-url ./ --no-cache --out-dir ../dist/ --no-source-maps

And then you can run parcel index.html from your uibuilder/[proejct name]/src file

Ok, I'll have a look at the files you sent... maybe tomorrow.. it's getting late now :slight_smile:

not when you restart the server .. just when you make changes to you .vue or .js files so the'll get rebuilt

cool .. see the files and if there anything just ask
most propably i wont know it and i'll have to look it up on the net :wink: