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, '<', '<' )
str = string.gsub( str, '>', '>' )
str = string.gsub( str, '"', '"' )
str = string.gsub( str, ''', "'" )
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, '&', '&' ) -- 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
|