LogicMachine Forum
Script integration for Acrylic Amp & Upstream audio device - Printable Version

+- LogicMachine Forum (https://forum.logicmachine.net)
+-- Forum: LogicMachine eco-system (https://forum.logicmachine.net/forumdisplay.php?fid=1)
+--- Forum: Scripting (https://forum.logicmachine.net/forumdisplay.php?fid=8)
+--- Thread: Script integration for Acrylic Amp & Upstream audio device (/showthread.php?tid=4519)



Script integration for Acrylic Amp & Upstream audio device - tigi - 20.01.2023

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