[APP][Pro] NEW: Dashboard Studio - A completely free-form dashboard designer

Haha, thanks. But the reason is in here:

It is because you guys are the testing department :rofl:

I shall fix this :+1:

Ctrl + G is a standard browser shortcut for “Find Next,” and it is usually a good idea to avoid overriding those default keys. Could you clarify the specific friction you are experiencing? Are you looking for a more efficient way to navigate through a large number of pages?

I was thinking of this. But you could just use the slider widget and attach the value using a local topic to the “move Y” of the image widget :rofl:

It works, but it is not really responsive since the slider only sends new data on release.
Dashboard snippet:

{
  "snippetHeader": "Dashboard Studio Snippet",
  "snippetType": "widget-snippet",
  "snippetFormatVersion": 1,
  "version": "1.4.1",
  "source": {
    "dashboardName": "test5",
    "page": 1,
    "createdAt": "2026-03-20T13:25:29.915Z"
  },
  "master": {
    "currentPage": 1
  },
  "widgets": {
    "image_4028": {
      "type": "image",
      "overrides": {
        "x": 340,
        "y": 200,
        "width": 480,
        "height": 480,
        "zIndex": 10,
        "page": 1,
        "sourceType": "svg",
        "url": "images/placeholder.png",
        "svgRaw": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 600 1540\" width=\"100%\" height=\"100%\">\n  <style>\n    .hdr { font-family: sans-serif; font-size: 16px; font-weight: bold; fill: #ffffff; text-anchor: middle; dominant-baseline: middle; }\n    .txt { font-family: sans-serif; font-size: 14px; fill: #333333; text-anchor: middle; dominant-baseline: middle; }\n  </style>\n\n  <rect x=\"0\" y=\"0\" width=\"600\" height=\"40\" fill=\"#2C3E50\"/>\n  <text x=\"100\" y=\"20\" class=\"hdr\">Day</text>\n  <text x=\"300\" y=\"20\" class=\"hdr\">Temperature (°C)</text>\n  <text x=\"500\" y=\"20\" class=\"hdr\">Condition</text>\n\n  <g transform=\"translate(0, 40)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 1</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#C1E1C1\"/><text x=\"300\" y=\"15\" class=\"txt\">12</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#E2E2E2\"/><text x=\"500\" y=\"15\" class=\"txt\">Partly Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 70)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 2</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#C1E1C1\"/><text x=\"300\" y=\"15\" class=\"txt\">14</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"500\" y=\"15\" class=\"txt\">Sunny</text>\n  </g>\n  <g transform=\"translate(0, 100)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 3</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#C1E1C1\"/><text x=\"300\" y=\"15\" class=\"txt\">11</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#CFCFCF\"/><text x=\"500\" y=\"15\" class=\"txt\">Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 130)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 4</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#A7C7E7\"/><text x=\"300\" y=\"15\" class=\"txt\">9</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#A7C7E7\"/><text x=\"500\" y=\"15\" class=\"txt\">Rainy</text>\n  </g>\n  <g transform=\"translate(0, 160)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 5</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#C1E1C1\"/><text x=\"300\" y=\"15\" class=\"txt\">13</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#E2E2E2\"/><text x=\"500\" y=\"15\" class=\"txt\">Partly Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 190)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 6</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#C1E1C1\"/><text x=\"300\" y=\"15\" class=\"txt\">15</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"500\" y=\"15\" class=\"txt\">Sunny</text>\n  </g>\n  <g transform=\"translate(0, 220)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 7</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"300\" y=\"15\" class=\"txt\">18</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"500\" y=\"15\" class=\"txt\">Sunny</text>\n  </g>\n  <g transform=\"translate(0, 250)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 8</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"300\" y=\"15\" class=\"txt\">20</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#E2E2E2\"/><text x=\"500\" y=\"15\" class=\"txt\">Partly Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 280)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 9</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"300\" y=\"15\" class=\"txt\">17</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#CFCFCF\"/><text x=\"500\" y=\"15\" class=\"txt\">Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 310)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 10</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"300\" y=\"15\" class=\"txt\">16</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#A7C7E7\"/><text x=\"500\" y=\"15\" class=\"txt\">Rainy</text>\n  </g>\n  <g transform=\"translate(0, 340)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 11</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"300\" y=\"15\" class=\"txt\">19</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#E2E2E2\"/><text x=\"500\" y=\"15\" class=\"txt\">Partly Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 370)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 12</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"300\" y=\"15\" class=\"txt\">22</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"500\" y=\"15\" class=\"txt\">Sunny</text>\n  </g>\n  <g transform=\"translate(0, 400)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 13</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FAC898\"/><text x=\"300\" y=\"15\" class=\"txt\">24</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"500\" y=\"15\" class=\"txt\">Sunny</text>\n  </g>\n  <g transform=\"translate(0, 430)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 14</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FAC898\"/><text x=\"300\" y=\"15\" class=\"txt\">25</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"500\" y=\"15\" class=\"txt\">Sunny</text>\n  </g>\n  <g transform=\"translate(0, 460)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 15</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FAC898\"/><text x=\"300\" y=\"15\" class=\"txt\">28</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#E2E2E2\"/><text x=\"500\" y=\"15\" class=\"txt\">Partly Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 490)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 16</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FF6961\"/><text x=\"300\" y=\"15\" class=\"txt\">30</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"500\" y=\"15\" class=\"txt\">Sunny</text>\n  </g>\n  <g transform=\"translate(0, 520)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 17</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FF6961\"/><text x=\"300\" y=\"15\" class=\"txt\">32</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"500\" y=\"15\" class=\"txt\">Sunny</text>\n  </g>\n  <g transform=\"translate(0, 550)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 18</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FF6961\"/><text x=\"300\" y=\"15\" class=\"txt\">31</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#E2E2E2\"/><text x=\"500\" y=\"15\" class=\"txt\">Partly Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 580)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 19</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FF6961\"/><text x=\"300\" y=\"15\" class=\"txt\">29</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#B4B4CC\"/><text x=\"500\" y=\"15\" class=\"txt\">Stormy</text>\n  </g>\n  <g transform=\"translate(0, 610)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 20</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FAC898\"/><text x=\"300\" y=\"15\" class=\"txt\">26</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#A7C7E7\"/><text x=\"500\" y=\"15\" class=\"txt\">Rainy</text>\n  </g>\n  <g transform=\"translate(0, 640)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 21</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FAC898\"/><text x=\"300\" y=\"15\" class=\"txt\">23</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#CFCFCF\"/><text x=\"500\" y=\"15\" class=\"txt\">Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 670)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 22</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FAC898\"/><text x=\"300\" y=\"15\" class=\"txt\">25</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#E2E2E2\"/><text x=\"500\" y=\"15\" class=\"txt\">Partly Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 700)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 23</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FAC898\"/><text x=\"300\" y=\"15\" class=\"txt\">27</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"500\" y=\"15\" class=\"txt\">Sunny</text>\n  </g>\n  <g transform=\"translate(0, 730)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 24</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FF6961\"/><text x=\"300\" y=\"15\" class=\"txt\">29</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"500\" y=\"15\" class=\"txt\">Sunny</text>\n  </g>\n  <g transform=\"translate(0, 760)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 25</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FF6961\"/><text x=\"300\" y=\"15\" class=\"txt\">33</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"500\" y=\"15\" class=\"txt\">Sunny</text>\n  </g>\n  <g transform=\"translate(0, 790)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 26</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FF6961\"/><text x=\"300\" y=\"15\" class=\"txt\">34</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#E2E2E2\"/><text x=\"500\" y=\"15\" class=\"txt\">Partly Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 820)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 27</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FF6961\"/><text x=\"300\" y=\"15\" class=\"txt\">30</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#B4B4CC\"/><text x=\"500\" y=\"15\" class=\"txt\">Stormy</text>\n  </g>\n  <g transform=\"translate(0, 850)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 28</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FAC898\"/><text x=\"300\" y=\"15\" class=\"txt\">26</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#A7C7E7\"/><text x=\"500\" y=\"15\" class=\"txt\">Rainy</text>\n  </g>\n  <g transform=\"translate(0, 880)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 29</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"300\" y=\"15\" class=\"txt\">22</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#CFCFCF\"/><text x=\"500\" y=\"15\" class=\"txt\">Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 910)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 30</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"300\" y=\"15\" class=\"txt\">20</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#E2E2E2\"/><text x=\"500\" y=\"15\" class=\"txt\">Partly Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 940)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 31</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"300\" y=\"15\" class=\"txt\">21</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"500\" y=\"15\" class=\"txt\">Sunny</text>\n  </g>\n  <g transform=\"translate(0, 970)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 32</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FAC898\"/><text x=\"300\" y=\"15\" class=\"txt\">24</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#E2E2E2\"/><text x=\"500\" y=\"15\" class=\"txt\">Partly Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 1000)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 33</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"300\" y=\"15\" class=\"txt\">22</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#CFCFCF\"/><text x=\"500\" y=\"15\" class=\"txt\">Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 1030)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 34</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"300\" y=\"15\" class=\"txt\">18</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#A7C7E7\"/><text x=\"500\" y=\"15\" class=\"txt\">Rainy</text>\n  </g>\n  <g transform=\"translate(0, 1060)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 35</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"300\" y=\"15\" class=\"txt\">16</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#A7C7E7\"/><text x=\"500\" y=\"15\" class=\"txt\">Rainy</text>\n  </g>\n  <g transform=\"translate(0, 1090)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 36</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#C1E1C1\"/><text x=\"300\" y=\"15\" class=\"txt\">15</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#CFCFCF\"/><text x=\"500\" y=\"15\" class=\"txt\">Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 1120)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 37</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#C1E1C1\"/><text x=\"300\" y=\"15\" class=\"txt\">13</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#E2E2E2\"/><text x=\"500\" y=\"15\" class=\"txt\">Partly Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 1150)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 38</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#C1E1C1\"/><text x=\"300\" y=\"15\" class=\"txt\">14</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"500\" y=\"15\" class=\"txt\">Sunny</text>\n  </g>\n  <g transform=\"translate(0, 1180)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 39</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#C1E1C1\"/><text x=\"300\" y=\"15\" class=\"txt\">12</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#CFCFCF\"/><text x=\"500\" y=\"15\" class=\"txt\">Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 1210)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 40</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#A7C7E7\"/><text x=\"300\" y=\"15\" class=\"txt\">9</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#A7C7E7\"/><text x=\"500\" y=\"15\" class=\"txt\">Rainy</text>\n  </g>\n  <g transform=\"translate(0, 1240)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 41</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#A7C7E7\"/><text x=\"300\" y=\"15\" class=\"txt\">8</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#B4B4CC\"/><text x=\"500\" y=\"15\" class=\"txt\">Stormy</text>\n  </g>\n  <g transform=\"translate(0, 1270)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 42</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#C1E1C1\"/><text x=\"300\" y=\"15\" class=\"txt\">10</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#A7C7E7\"/><text x=\"500\" y=\"15\" class=\"txt\">Rainy</text>\n  </g>\n  <g transform=\"translate(0, 1300)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 43</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#C1E1C1\"/><text x=\"300\" y=\"15\" class=\"txt\">11</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#CFCFCF\"/><text x=\"500\" y=\"15\" class=\"txt\">Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 1330)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 44</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#C1E1C1\"/><text x=\"300\" y=\"15\" class=\"txt\">13</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#E2E2E2\"/><text x=\"500\" y=\"15\" class=\"txt\">Partly Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 1360)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 45</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#C1E1C1\"/><text x=\"300\" y=\"15\" class=\"txt\">15</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#FDFD96\"/><text x=\"500\" y=\"15\" class=\"txt\">Sunny</text>\n  </g>\n  <g transform=\"translate(0, 1390)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 46</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#C1E1C1\"/><text x=\"300\" y=\"15\" class=\"txt\">14</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#E2E2E2\"/><text x=\"500\" y=\"15\" class=\"txt\">Partly Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 1420)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 47</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#C1E1C1\"/><text x=\"300\" y=\"15\" class=\"txt\">12</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#CFCFCF\"/><text x=\"500\" y=\"15\" class=\"txt\">Cloudy</text>\n  </g>\n  <g transform=\"translate(0, 1450)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 48</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#C1E1C1\"/><text x=\"300\" y=\"15\" class=\"txt\">10</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#A7C7E7\"/><text x=\"500\" y=\"15\" class=\"txt\">Rainy</text>\n  </g>\n  <g transform=\"translate(0, 1480)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#FAFAFA\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 49</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#A7C7E7\"/><text x=\"300\" y=\"15\" class=\"txt\">9</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#A7C7E7\"/><text x=\"500\" y=\"15\" class=\"txt\">Rainy</text>\n  </g>\n  <g transform=\"translate(0, 1510)\">\n    <rect x=\"0\" width=\"200\" height=\"30\" fill=\"#EFEFEF\"/><text x=\"100\" y=\"15\" class=\"txt\">Day 50</text>\n    <rect x=\"200\" width=\"200\" height=\"30\" fill=\"#A7C7E7\"/><text x=\"300\" y=\"15\" class=\"txt\">7</text>\n    <rect x=\"400\" width=\"200\" height=\"30\" fill=\"#CFCFCF\"/><text x=\"500\" y=\"15\" class=\"txt\">Cloudy</text>\n  </g>\n</svg>\n",
        "moveX": 0,
        "moveY": 113,
        "label": "",
        "metaName": "New Image",
        "scale": 203,
        "fit": false,
        "proportional": false
      },
      "bindings": {
        "moveY": "local/scrollbarY"
      }
    },
    "slider_7069": {
      "type": "slider",
      "overrides": {
        "x": 830,
        "y": 200,
        "width": 60,
        "height": 480,
        "zIndex": 10,
        "page": 1,
        "value": 370,
        "min": -370,
        "max": 370,
        "allowDecimals": false,
        "vertical": true,
        "showVal": false,
        "valOffX": 0,
        "valOffY": 0,
        "valAlign": "center",
        "outputTopic": "local/scrollbarY",
        "label": "",
        "labelOffX": 0,
        "labelOffY": 0,
        "metaName": "New Slider",
        "colFill": "#334155"
      },
      "bindings": {}
    },
    "label_5798": {
      "type": "label",
      "overrides": {
        "x": 330,
        "y": 130,
        "width": 570,
        "height": 560,
        "zIndex": 9,
        "page": 1,
        "text": "Scrollbar that scrolls an SVG table",
        "metaName": "text",
        "labelCase": "none",
        "textVerticalAlign": "top",
        "padding": 2
      },
      "bindings": {}
    }
  }
}
1 Like

Nice case @Satoer !
Showing:

  • local variable
  • snippet usage
  • raw SVG
  • a solution for my image scroll question
  • built with yet unrelease v1.4.1

Hoping for a nice snippet library built by multiple dash users.

New TEST release V1.4.1

Haha, well… released now :rofl:
Added an option to the slider to directly send data on movement. But I recommend to use this only with local topic. (like for using this as a scrollbar :wink: )

Found an ipad Air 2 test device running iOS 12.4


Got it running :+1:
The dashboard works well enough, but the editor is a bit of a mess. The editor is not intended for iPad use anyway so should be no problem. Give it a shot and let me know how it runs on your iPad Air 1. I’m guessing the performance won’t be great since the Air 2 handles it fine, but the Air 1 only has half the RAM (1 GB vs 2 GB) and half the CPU power (4 cores instead of 8). I’d also suggest avoiding those curved animated arrows. There is definitely room for improvement, though. Dashboard Studio basically runs the entire app in the browser, so for the sake of these older devices, it might be worth pulling it apart and separating the dashboard viewer from the editor. That said, it’s a pretty massive architectural change and probably a lot of work just to support a 13-year-old device! :sweat_smile:

Fixed :+1:

This is really great! Phenomenal progress in less than two months!! Bravo :clap:

I’m currently using a few different devices for dashboards (tablets, NSPanel Pro, NSPanel Pro 120). Sharptools has been my go-to so far – however, I’m always bothered by the latency and the fact that the dashboards are stored in the cloud. So DashboardStudio is definitely worth considering. While experimenting, I noticed that:

  • a dashboard can’t be bound to a specific device or device type. This is primarily due to the different resolutions, and besides, I don’t like the idea of ​​my kids controlling my office from their room :wink:
  • The NSPanel Pro 120 has a resolution of 750x1334, which I’ve configured accordingly. Despite this, the dashboard is still cut off.

If I’ve done something wrong, please let me know. I’ve only just started experimenting.

Thanks Wolfgang

All dashboards have indeed access to every device capability activated in the Homey DS configuration. However, the only way someone can actually control your office devices is if they manually add the widgets for those specific devices to their dashboard. I assume your kids have their own personal dashboards set up?

There is an option inside the Homey DS config to hide the edit button:


Unless your kids are real script kiddies and know how to “hack” the CSS behind it, I think your office devices are safe.

Many devices have both a native resolution and a logical resolution. For example, the iPad has a massive native resolution of 2388x1668, but its logical resolution is only 834x1194. If websites and text were displayed at the native resolution, everything would be way too small. Using the logical resolution keeps the website at a normal scale while still displaying sharp lines and crisp text. I have no idea what the logical resolution is of this NSPanel, I guess you need to experiment with that :sweat_smile:

I’d love to see a few pictures of your NSPanel devices once your dashboard is all set up.

Wow, you’re really putting your mind to it! I’ll gladly continue experimenting and send you the screenshots when the time comes.

Nice solution!
Currently, it is technically no longer necessary to develop a Table widget because of the SVG solution. However, for the sake of user-friendliness, a Table widget would be desirable in the long term.

I was confused by the “group/container” widget. I thought you have to use a group widget to use the grouping mechanism, which is not the case. I would rename that widget into “Container” preventing the usage of “group” for 2 different functions.

Thanks. I would like to create some kind of user gallery. Especially when less common devices are used for Dashboard Studio :slightly_smiling_face:

I would start at 375 x 667 pixels. (Half the native resolution)

But what would be an easy way for a user to fill this table? Especially when you also want to set the cell colors. Working with massive JSON structures isn’t exactly fun or intuitive. Plus, what kind of data would someone even put in there? I understand your use case, but this is not something a regular, or even an advanced user would do I think. It should be data that comes natively from Homey. For instance, the Insight card generates a JSON format that the graph already understands, so the user can connect it directly to the topic without needing to know anything about the underlying data structure. It needs to be something along those lines. An advanced user could always script their own table if they really want to, but the base version should absolutely have a simple, ready to go use-case option.

Yes, thought of that also. Excellent point.

Maybe I should just name it back to that “Empty” again hahaha :rofl:

Hi @Satoer Not sure if I missed a setting, but there is something I would really like to have for “aesthetic” reasons: Gauge widgets have an icon and a value. When I use them side by side, there is always a fixed gap between them. However, when I set the icon to the left and the value to the right. I would like to have the value right aligned to the widget boundary. As an example, see below widget. I’d like all values xxxxW the be right aligned, while the icon stays to the left.

Hi.

I have made my first Dashboard and am really impressed with the tool. One question though :smile: .

Is it possible to switch off the ‘Toggle Edit Mode’ gear wheel in the bottom right corner? Maybe this is possible via the URL parameters?

It is in the Dashboard Studio settings inside Homey:

You aren’t missing anything, it’s not there. In a future update, I really want to streamline all the font settings since it’s honestly a bit of a mess right now. The goal is to have all text items share the exact same adjustable settings in a dedicated dialogue or dropdown menu, covering things like font, weight, size, alignment, and x and y offsets.

Perfect. I did not think to look there.

Thank you.

# NEW Test Release V1.4.2!

New “Reload Dashboard” “Then” card:

I Love the remote reload option in Kiosk browser, but now it’s built right into Dashboard Studio natively, which means it should work in any browser you’re using. Once you save a dashboard, you trigger the “Dashboard Saved” flow card within a Homey Flow. Just attach that to a “Reload Dashboard” card and drop the dashboard tag from the “Dashboard Saved” card into the Dashboard name setting. Now, your dashboard refreshes automatically! This is a lifesaver for screens in hard-to-reach spots or setups without a keyboard. Note: The refresh won’t trigger on a dashboard where the editor is open, so you don’t have to worry about the page reloading and closing your session.

Make sure you connect the “Dashboard Saved” WHEN card. Not the “Dashboard Loaded / Refreshed” WHEN card, because this will trigger an endless loop!

Renamed Group / Container widget:

Agreed and done :+1:

Added value text alignment option:

Added the option to left/center/right align the value (and unit) :+1:

It defaults to “center” when the position (Appearance section) is bottom and up, and to “left” when this is “left” of “right”. If the value is changed it keeps this change until the value is reset with the reset button.

Unless I hear about any major issues with this release, I’m planning to push this version to stable tonight.

2 Likes

Minor remark: in the widget dropdown the Container option is still on the G position of Group.

1 Like

Maybe see view port result at: What is my viewport

Thanks @Satoer !

Hello @Satoer

First of all congratulations for the application I’m looking at at the moment. Is it possible to have the camera streaming because it is something relatively used on dashboards?

If so, which format

Thx

Thanks!

Showing an RTSP or ONVIF stream in a web browser is incredibly difficult without specialized plugins. Interestingly, Homey recently developed a solution that transcodes camera streams in real time to WebRTC, which is much more browser-friendly. However, I don’t think access to those streams is available for third-party developers just yet (Athom? :folded_hands: ).

If your camera supports snapshots, you can actually create a “pseudo” live stream by sending a sequence of image URLs to the image widgets. It works surprisingly well, even if the framerate is a bit limited.

See the tips and tricks section of the help file and read “Streaming a live camera feed”

1 Like