Music/Player control for basic UI (Squeezebox)

Hi all,

inspired by Custom Widget: Music Control I started to port such a widget to my classic ui interface:

Version 1.1:

  • Initial support for album covers (only displayed if enough space is available)
  • Initial work done to add multiplayer support

This currently requires habpanel to be installed; for some .css - but that could be removed. It’s an early prototype - I’ll updated the post if I enhance it. But it might be useful already:

sitemap:

Switch item=ItemPlayRandomTitles
             
Webview url="/static/Player/Player.html" height=5
Webview url="/static/Player/Player.html" height=2

Slider item=GroupRadioVolume

openHAB.js:

// ***
// * Required definitions:
// *  - var baseURL = "../";
// *
// ***
// * Required API
// * - Google jQuery: https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// ***

function _GetData(itemUrl) {
    var itemValue = null;
    $.ajax({
        contentType: 'text/plain',
        url: itemUrl,
        data: {},
        async: false,
        success: function (data) {
            if (data != "NULL") {
                itemValue = data;
            }
        }
    });

    return itemValue;
}

// JSON to CSV Converter
function _ConvertToCSV(objArray) {
    var array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray;
    var str = '';

    for (var i = 0; i < array.data.length; i++) {
        var line = '';
        for (var index in array.data[i]) {
            if (line != '') line += ','

            line += array.data[i][index];
        }

        str += line + '\r\n';
    }

    return str;
}

function GetOpenHABItem(item) {
    var itemUrl = baseURL.concat("rest/items/").concat(item).concat("/state/");
    var itemValue = null;

    return _GetData(itemUrl)
}

function GetOpenHABItemIntValue(item) {
    return Math.round(GetOpenHabItem(item));
}

function GetOpenHABItemHistoryJSON(item) {
    var itemUrl = baseURL.concat("rest/persistence/items/").concat(item);
    var jsonData = null;

    jsonData = _GetData(itemUrl);
    return jsonData;
}

function GetOpenItemHABHistoryCSV(item) {
    var jsonData = GetOpenHABItemHistoryJSON(item);
    return _ConvertToCSV(jsonData);
}

function GetParameter(parameterName) {
    var result = null,
        tmp = [];
    location.search.substr(1).split("&")
        .forEach(function (item) {
          tmp = item.split("=");
          if (tmp[0] === parameterName) { 
              result = decodeURIComponent(tmp[1]);
          }
        }
      );
    return result;
}

Player.html:

<html>

<head>
  <!-- HABPanel Styles -->
  <link rel="stylesheet" href="../../habpanel/vendor/vendor.min.css" />
  <link rel="stylesheet" href="../../habpanel/assets/styles/themes/default/default.min.css" />

  <!-- MusicControl Widget Styles -->
  <style>
    #main {
      height: 100%;
      width: 100%;
      display: table;
    }

    #container {
      display: table-cell;
      height: 100%;
      vertical-align: middle;
    }

    .btn-group {
      top: -40px;
    }

    .btn-pad {
      padding: 15px 40px;
      opacity: 0.85;
    }

    .btn-radius {
      padding: 5px 5px;
      border-radius: 50% !important;
      background-color: #d23f31;
      height: 56px;
      width: 56px;
      box-shadow: 0 0 4px rgba(0, 0, 0, .14), 0 4px 8px rgba(0, 0, 0, .28);
      box-sizing: content-box;
      margin-left: -13px;
      position: relative;
      z-index: 1000;
      cursor: pointer;
    }

    .btn-red {
      background-color: red;
    }

    .button-main {
      position: relative;
      top: 10;
    }

    .glyphicon-text {
      font-size: xx-large;
      padding: 0px;
    }

    .btn-lf {
      position: relative;
      top: -10px;
      left: 8px;
      background-color: #fff;
      box-shadow: 0 0 4px rgba(0, 0, 0, .14), 0 4px 8px rgba(0, 0, 0, .28);
      color: #000;
      border-top-left-radius: 10%;
      border-bottom-left-radius: 10%;
      cursor: pointer;
    }

    .btn-lf:hover {
      background-color: #eaeaea;
      color: #fff;
    }

    .btn-rt {
      position: relative;
      top: -10px;
      right: 8px;
      margin-left: -13px;
      background-color: #fff;
      box-shadow: 0 0 4px rgba(0, 0, 0, .14), 0 4px 8px rgba(0, 0, 0, .28);
      color: #000;
      border-top-right-radius: 10%;
      border-bottom-right-radius: 10%;
      cursor: pointer;
    }

    .btn-rt:hover {
      background-color: #eaeaea;
      color: #fff;
    }

    .btn-info {
      vertical-align: bottom;
    }

    .btn-warning {
      vertical-align: bottom;
    }


    .name-text {
      text-align: center;
      display: block;
    }

    .title-text {
      font-size: x-large;
      text-align: center;
      display: block;
    }

    .div-slider {
      width: 220px;
      margin: 0 auto;
    }

    html,
    body,
    label {
      color: #fff;
      text-align: center;
      background: inherit;
    }

    img {
      height: 150px;
      width: 150;
      position: relative;
      z-index: 0;
      display: block;
      top: 40;
      left: 40;
      border-top-right-radius: 10%;
      border-top-left-radius: 10%;
      border-bottom-right-radius: 10%;
      border-bottom-left-radius: 10%;
    }
  </style>

  <script type="text/javascript">
    var baseURL = "../../";
    var squeezeBoxURL = "http://HAUS:9000/status.html";
  </script>
  <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
  <script type="text/javascript" src="../javascript/openHAB.js"></script>
  <!--Buttons: 

"brightness_down", "brightness_toggle", "brightness_up", "done", "down", "fire", "jump_fwd", "jump_rew", "left", "left_repeat", 
"menu_browse_album", "menu_browse_artist", "menu_browse_genre", "menu_browse_music", "menu_browse_playlists", "menu_plugins", 
"menu_home", "menu_now_playing", "menu_playlist", "menu_pop", "menu_search_album", "menu_search_artist", 
"menu_search_song", "menu_settings", "muting", "numberScroll_0", "numberScroll_1", "numberScroll_2", 
"numberScroll_3", "numberScroll_4", "numberScroll_5", "numberScroll_6", "numberScroll_7", "numberScroll_8", 
"numberScroll_9", "pause", "play", "play_0", "play_1", "playdisp_0", "playdisp_1", "playdisp_2", 
"playdisp_3", "playdisp_4", "playdisp_5", "playdisp_toggle", "power_off", "power_on", "power_toggle", 
"repeat_0", "repeat_1", "repeat_2", "repeat_toggle", "right", "right", "scan_end", "scan_fwd", 
"scan_rew", "search", "shuffle_off", "shuffle_on", "shuffle_toggle", "sleep", "slimtrisButtonHandler_add", 
"slimtrisButtonHandler_down", "slimtrisButtonHandler_left", "slimtrisButtonHandler_play", "slimtrisButtonHandler_right", 
"slimtrisButtonHandler_up", "stop", "textsize_large", "textsize_small", "textsize_medium" (on displays that support it), 
"textsize_toggle", "titleFormat", "up", "volume_down", & "volume_up". 
-->

  <script type="text/javascript">
    var imageSrc = "http://192.168.0.73:9000/music/current/cover.jpg";
    // ?player=" + GetOpenHABItem("ItemAudioCurrentPlayerId");
    var showCover = false;
    
    $(document).ready(function () {
      document.getElementById("play").style.display = "none";

      var pageHeight = window.innerHeight;
      var pageWidth  = window.innerWidth;
      var pageElement = document.getElementById("main");

      // only show cover if there is enough space ...
      if (pageHeight < 180) {
        showCover = false;
        document.getElementById("cover").style.display = "none";
        document.getElementById("widget").style.top = "0px";
      } else { 
        showCover = true; 
      }

      pageElement.style.height = pageHeight + "px";
      pageElement.style.width  = pageWidth  + "px";

      pageElement = document.getElementById("container");

      pageElement.style.height = pageHeight + "px";
      pageElement.style.width  = pageWidth + "px";

      document.body.style.height = pageHeight + "px";
      document.body.style.width  = pageWidth + "px";

      setInterval(function () {
        updateCover();
      }, 5000);
    })

    /***
     * Update cover image ...
     */
    function updateCover()  {
      var _date = new Date();
      var _element = document.getElementById("cover");
      // Add cache breaker to image URL; temporary hack until we know how
      // to get the full cover URL without the proxy URL.
      _element.src = imageSrc + "?update=" + _date.getTime(); 
    }

    /***
     * Return the player id; if you only control a single player you
     * may hardcode the MAC address. If more than one player is used we load
     * the current active MAC from OH.
     */
    function getPlayerId() {
      return GetOpenHABItem("ItemAudioCurrentPlayerId");
    }

    function next() {
      $.get(squeezeBoxURL + "?p0=button&p1=jump_fwd&player=" + getPlayerId());
    }
    function prev() {
      $.get(squeezeBoxURL + "?p0=playlist&p1=index&p2=jump_rew&player=" + getPlayerId());
    }
    function pause() {
      document.getElementById("play").style.display = "inherit";
      document.getElementById("pause").style.display = "none";

      $.get(squeezeBoxURL + "?p0=play&player=" + getPlayerId());
    }
    function play() {
      document.getElementById("play").style.display = "none";
      document.getElementById("pause").style.display = "inherit";

      $.get(squeezeBoxURL + "?p0=pause&player=" + getPlayerId());
    }
  </script>
</head>

<body>
  <div id="main">
    <div id="container">
      <!--
    <label class="name-text" >{{config.my_name}}</label>
    <label class="title-text">{{itemValue(config.inputsource_item)}}</label>
    <label class="title-text">{{itemValue(config.song_title)}}</label>
    <label class="name-text" >{{itemValue(config.song_artist)}} {{itemValue(config.song_album)}}</label>
    -->

      <div id="widget" class="btn-group">
        <img id="cover"></img>
        <label class="btn-pad btn-lf" onclick="prev()">
        <i class="glyphicon glyphicon-step-backward"></i>
      </label>
        <label id="play" class="btn-radius btn-info" onclick="play()">
        <i class="glyphicon glyphicon-play glyphicon-text button-main"></i>
      </label>
        <label id="pause" class="btn-radius btn-warning" onclick="pause()">
        <i class="glyphicon glyphicon-pause glyphicon-text button-main" ></i>
      </label>
        <label class="btn-pad btn-rt" onclick="next()">
        <i class="glyphicon glyphicon-step-forward"></i>
      </label>
      </div>
      <!--
    <label class="name-text">({{itemValue(config.song_playingtime)}})</label>
      -->
    </div>
  </div>
</body>

</html>

with kind regards,
Patrik

4 Likes

That is very nice looking - great job!

Can you give any insight on how to implement this? Is it a webview element in the sitemap?

Hi @pahansen,

thanks for your interest :slight_smile: - currently the thing is still quite hack. As I go along I’ll try to make it more generic an easier to use.

Anyhow … it is to be used as a web view. You need “habpanel” ui to be installed for the moment, as it uses some styles from there.

Some questions:

  • Do you have already some experience with were to place .html elements?
  • Do you have one, or multiple players you need to control?

with kind regards,
Patrik

Yeah, i’ve git a few webview’s set up for other stuff in my sitemap so know their general use.

I’ve got a couple of different sonos players, but it might be a nice way to also control my Plex player too… I’ll need to look into that.

@pahansen

Then I recommend to copy the code & have a look into it if - if you have specific questions I try to help.

Hi, I’m new to OpenHub, I’m trying to do everything according to your instructions, but I can not do anything, the item is not displayed in the androidapp, i can see only an empty frame, can you help me with this problem?
Thank you

Hi @Vladimir,

unfortunately web elements do not work in the apps as far as I know; I use basic ui in a web browser/kiosk mode on my devices.

Also the example will not work with OK 2.1 anymore, as stylesheets it used from habpanel are no longer present/at the same place.

If you go for the basic ui approach I recommend to have a look at

on how to inject custom css; then also the default controls (which are compatible with the apps) can be used. But the style will only work in the browser as well.

with kind regards,
Patrik