This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

Script integration for Acrylic Amp & Upstream audio device
#1
This script is still a work in progress, there is some unused code and optimization is certainly possible, but it already functions.

This script makes it able to control (on/off, switch source, volume) of your Acrylic Amp4 device from pushbuttons on a wall switch 
You can custom define 5 web radio stations and the text that will be displayed on the pushbutton 

Prerequisites:
  • An Acrylic Amp4/Upstream audio device connected to your lan and speakers
       
  • A zigbee, knx, enocean,... relay switch, can be a controllable AC plug
       
  • A pushbutton that can display custom text such as MDT Glasstaster (II) Smart
    Though any pushbutton will work but without station name feedback
       

The script needs 4 pushbuttons, these related group addresses have a tag attached such as 'audio_bath':
  • 1 for on/off
  • 1 for switching between sources/stations
  • 2 for up/down volume

Event script triggered by tag such as 'audio_bath'
Code:
-- https://developer.arylic.com/httpapi/#http-api
ip = '192.168.1.100' -- IP Address of Acrylic Upstream AMP Device
require('json')
require('user.up2stream')
require('socket.http')
local DEBUG = false
-- list of stations to choose from, limited to 5 radio stations and 2 special sources (usb and wifi)
-- special care for usb and wifi naming which cannot be renamed since used in script to construct the correct http api url
-- though they can be replaced by 2 extra radiostations, in the 4Stream Android app you can change source also
local stations = {
 
  {'Radio2','http://icecast.vrtcdn.be/ra2ovl-high.mp3'},
  {'QMusic','https://playerservices.streamtheworld.com/api/livestream-redirect/QMUSIC.mp3'},
  {'Nostalgie','http://playerservices.streamtheworld.com/api/livestream-redirect/NOSTALGIEWHATAFEELINGAAC.aac'},
  {'StuBru','http://icecast.vrtcdn.be/stubru-high.mp3'},
  {'ZenFM','https://23613.live.streamtheworld.com/TOPZEN.mp3'},
  {'usb','3'},
  {'wifi','wifi'}
  }

local StationSelected -- The Current selected station number, based on Table Key number
local channelname -- The Channel name, the first nested table field from stations table
local grp_onoff_obj = '5/0/1' -- Button on/off object for device
local grp_onoff_FB_obj = '5/1/1' -- Button on/off Feedback object for device
local grp_channelswitch_obj = '5/0/2' -- Button channel switching object for device
local grp_channelswitch_FB_obj = '5/1/2' -- Button channel switching Feedback object for device
local grp_volumeUP_obj = '5/0/4' -- Button Volume UP object for device
local grp_volumeDOWN_obj = '5/0/5' -- Button Volume DOWN object for device
local grp_volumeUP_FB_obj = '5/1/4' -- Button Volume UP Feedback object for device
local grp_volumeDOWN_FB_obj = '5/1/5' -- Button Volume DOWN Feedback object for device
local grp_switch_obj = '8/6/10' -- AC Plug Switch on/off object for device
local grp_trigger_obj = event.dst -- Grp object that triggered this script, since script is triggered by TAG we need to identify the grp object
local message_obj = '8/5/6' -- MDT Glass Pushbutton ASCII Message Grp Address to custom name pushbutton based on state, here used for displaying channel name / radio station
local RoomChannel = 'Channel_Badk'

AudioTb = storage.get('Audio_UpStream') -- Get audio table from storage (where last station is stored)
if type(AudioTb) ~= 'table' or next(AudioTb) == nil then -- if no table exists
  AudioTb = {} -- create an empty table
  AudioTb[RoomChannel] = 1 -- set initial station to 1
end

-- DEVICE ON/OFF BUTTON
if grp_trigger_obj == grp_onoff_obj then -- if on/off button is pressed
  if event.getvalue() then -- if group object is true / ON
    if DEBUG == true then log('activate') end
    grp.checkwrite(grp_switch_obj, true) -- switch AC plug to ON
    grp.checkwrite(grp_onoff_FB_obj, true) -- on/off group object feedback to ON
    grp.checkwrite(message_obj, '1/3') -- show waiting message above pushbutton
    pingdevice(ip) -- first check and wait till device is pingable
    grp.checkwrite(message_obj, '2/3')  -- see prev comment
    internetup() -- second check and wait till internet is up for device
    grp.checkwrite(message_obj, '3/3')  -- see prev comment
    os.sleep(2) -- Optionally give the device some extra time to be ready to receive to weblink
    StationSelected = AudioTb[RoomChannel] -- Load the last stored selected station from table
    -- Check which station was last used, if usb or wifi use specific api url string, else use webradio url string
    if stations[StationSelected][1] == 'usb' then -- if first field of nested table has 'usb' in it
      station = Up2_PlayUSB..stations[StationSelected][2] -- use specific link and construct on selected file in stations list
      channelname = stations[StationSelected][1] -- set channelname to 'usb' (from stations table list)
    elseif stations[StationSelected][1] == 'wifi' then -- if first field of nested table has 'wifi' in it
      station = setPlayerCmdSource..stations[StationSelected][2] -- use specific link and construct to play from 4Stream App or Spotify...
      channelname = stations[StationSelected][1] -- set channelname to 'wifi' (from stations table list)
    else -- if anything else than usb or wifi we assume its a web radio station
      station = setPlayerCmdWebRadio..stations[StationSelected][2]  -- use specific link and construct to play selected radio station from stations table list
      channelname = stations[StationSelected][1] -- set channelname to radio station name from stations table list
    end   
    grp.checkwrite(message_obj, channelname) -- show selected channelname message above pushbutton
    socket.http.TIMEOUT = 70
    socket.http.request(station) -- send command url to device to start playing the last stored station or source
  else -- if group object is false / OFF
    if DEBUG == true then log('uitschakelen') end
    grp.checkwrite(grp_onoff_FB_obj, false) -- set grp object device on/off feedback to false
    grp.checkwrite(grp_switch_obj, false)   -- set grp object AC plug on/off to false, switching off
    grp.checkwrite(message_obj, 'goodbye') -- show goodbye message above pushbutton
    os.sleep(3) -- wait for 3 seconds
    grp.checkwrite(message_obj, false) -- show nothing above pushbutton
    grp.checkwrite(grp_channelswitch_FB_obj, 0) -- set grp object channel button switch feedback to 0 / false
  end
end

-- CHANNEL SELECTION BUTTON
if grp_trigger_obj == grp_channelswitch_obj then -- if channel switch button is pressed
  grp.checkwrite(grp_channelswitch_FB_obj, 1) -- set channel switch group object feedback to 1 / on
  StationSelected = AudioTb[RoomChannel] + 1 -- get value from table/storage and add 1
  if StationSelected == 8 then StationSelected = 1 end -- if value is 8 reset to 1, the stations list is limited to 'only' 7 items

  if stations[StationSelected][1] == 'usb' then -- see prev comment
    station = Up2_PlayUSB..stations[StationSelected][2] -- see prev comment
    channelname = stations[StationSelected][1] -- see prev comment
  elseif stations[StationSelected][1] == 'wifi' then -- see prev comment
    station = setPlayerCmdSource..stations[StationSelected][2] -- see prev comment
    channelname = stations[StationSelected][1] -- see prev comment
  else -- see prev comment
    station = setPlayerCmdWebRadio..stations[StationSelected][2] -- see prev comment
    channelname = stations[StationSelected][1] -- see prev comment
  end

  if DEBUG == true then log(channelname,station) end

  socket.http.TIMEOUT = 70
  socket.http.request(station) -- see prev comment
 
  AudioTb[RoomChannel] = StationSelected -- update the table with the selected channel
    grp.checkwrite(message_obj, channelname) -- see prev comment
  os.sleep(1)
  grp.checkwrite(grp_channelswitch_FB_obj, 0) -- see prev comment
    storage.set('Audio_UpStream',AudioTb) -- write table to storage
 
end

-- VOLUME UP / DOWN BUTTONS
if grp_trigger_obj == grp_volumeUP_obj and grp.getvalue(grp_onoff_obj) == true then -- if UP button pressed and device is on
  grp.checkwrite(grp_volumeUP_FB_obj, 1) -- set volume up switch group object feedback to 1 / on
  grp_volume = tonumber(getdata('','vol')) -- get the current volume from device (function in user lib script)
  if grp_volume >= 80 then -- if volume is more than 80
        grp_volume = 80 -- keep volume at 80, is loud enough
  else
    grp_volume = grp_volume + 2 -- if not, set volume 2 notches higher
    end
  socket.http.TIMEOUT = 70
  socket.http.request(Up2_VolSet..grp_volume) -- send command url to device to set volume higher
  grp.checkwrite(grp_volumeUP_obj, 0) -- set volume up switch group object to 0 / off
  grp.checkwrite(grp_volumeUP_FB_obj, 0) -- set volume up switch group object feedback to 0 / off
elseif grp_trigger_obj == grp_volumeDOWN_obj and grp.getvalue(grp_onoff_obj) == true then -- if DOWN button pressed and device is on
  grp.checkwrite(grp_volumeDOWN_FB_obj, 1) -- set volume down switch group object feedback to 1 / on
  grp_volume = tonumber(getdata('','vol')) -- see prev comment
  if grp_volume <= 5 then -- if volume is less than 5
    grp_volume = 5 -- keep volume at 5, is quiet enough
  else
    grp_volume = grp_volume - 2 -- if not, set volume 2 notches lower
  end
  socket.http.TIMEOUT = 70
    socket.http.request(Up2_VolSet..grp_volume) -- send command url to device to set volume lower
  grp.checkwrite(grp_volumeDOWN_obj, 0) -- set volume down switch group object to 0 / off
  grp.checkwrite(grp_volumeDOWN_FB_obj, 0) -- set volume down switch group object feedback to 0 / off
end

User library script named user.up2stream
Code:
local Path = 'http://'..ip..'/httpapi.asp?command=' -- Link base path

-- in use
local getPlayerStatus = Path..'getPlayerStatus'
local getStatusEx = Path..'getStatusEx'
local setPlayerCmdSource = Path..'setPlayerCmd:switchmode:' -- add wifi, line-in, bleutooth, optical, pcusb, udisk, line-in2l
local setPlayerCmd = Path..'setPlayerCmd:'
local setPlayerCmdWebRadio = Path..'setPlayerCmd:play:'
local Up2_VolSet = Path..'setPlayerCmd:vol:'
local Up2_ShowVol = Path..'setPlayerCmd:Vol--n'
local Up2_PlayUSB = Path..'setPlayerCmd:playLocalList:' -- Add INDEX number (song number) to end
local Up2_SwitchPBSource = Path..'setPlayerCmd:switchmode:' --Add bluetooth, line-in, optical, udisk, wifi

-- Function to ping device with timeout (to see if device is already online after power-up)
function socketping(ip, port, timeout)
  port = port or 80
  local sock = require('socket').tcp()
  sock:settimeout(timeout or 20)
  local res, err = sock:connect(ip, port)
  --log(res)
  --log(err)
  sock:close()
  --status = err
  --return res, err
  if (res) then return true end
  return false
end

-- Function to replace a character in a string
-- Here used to get the group feedback address from the event group address that triggered this event
-- Only works when group address and group address feedback have the same first and last number such as 5/0/1 and 5/1/1
function replace_char(pos, str, r)
    return str:sub(1, pos-1) .. r .. str:sub(pos+1)
end

-- function to convert hex to character (for Acrylic http api)
function hextochar(str)
  local hex_to_char = {}
  for idx = 0, 255 do
    hex_to_char[("%02X"):format(idx)] = string.char(idx)
    hex_to_char[("%02x"):format(idx)] = string.char(idx)
  end
 
  str = str:gsub("(..)", hex_to_char)
  str = string.gsub( str, '&lt;', '<' )
  str = string.gsub( str, '&gt;', '>' )
  str = string.gsub( str, '&quot;', '"' )
  str = string.gsub( str, '&apos;', "'" )
  str = string.gsub( str, '&#(%d+);', function(n) return string.char(n) end )
  str = string.gsub( str, '&#x(%d+);', function(n) return string.char(tonumber(n,16)) end )
  str = string.gsub( str, '&amp;', '&' ) -- Be sure to do this after all others
  return str
end

function pingdevice(ip, timeout)
  local startTime = os.time()
  local online = false
  local i = 1
  if timeout == nil then timeout = 50 end
  while online == false and (os.time() - startTime < timeout) do
    local pingOutput = os.execute("ping -c 1 -W 1 " .. ip)
    -- Check ping successful by exit code (0 is success, anything else is failure)
    if pingOutput == 0 then
        online = true
       -- log("Audio Device is UP")
    else
        --log('countdown retry:'..(timeout-i))
        i = i + 1
    end
  end
  if online == false then
  --log("Audio Device is DOWN or timeout reached.")
  end
end

function internetup(timeout)
  --log('please wait')
  local startTime = os.time()
  local online = false
  local i = 1
  if timeout == nil then timeout = 60 end
  socket.http.TIMEOUT = 70
  while online == false and (os.time() - startTime < timeout) do
    data, status, err = socket.http.request(getStatusEx)
   -- log(status)
    if status == 200 and data ~= 'unknown command' then
      data = json.decode(data)
      local internet = tonumber(data.internet)
      if internet == 1 then
        online = true
        status = 'online'
        --log('device is online and has internet')
       
      end
    else
      --log('countdown: '..(timeout - i))
      os.sleep(1)
        i = i + 1
     
    end
  end
-- log(status)
end

function getdata(command, key)
  if command == nil then command = 'getPlayerStatus' end
  if key == nil then key = 'mode' end
    data, status, err = socket.http.request(getPlayerStatus)
    --log(status)
      data = json.decode(data)
      return data[key]
end
Reply


Forum Jump: