Lua Examples of accessing API's for Fronius, Sonos and Tech sterowniki

Discussion in 'C-Bus Automation Controllers' started by gte242, Oct 17, 2021.

  1. gte242

    gte242

    Joined:
    Apr 7, 2017
    Messages:
    31
    Likes Received:
    5
    Location:
    Poland
    Hi

    After a fun couple of days I thought I would share my Lua code for accessing Fronius Solar Inverters through the API and Tech Sterowniki (underfloor heating controllers. Both use request http command and return a json file which then can be queried and read the relevant values. I have included both API manuals as I have used only the data I need in the examples.

    Sonos - I converted the code from the other forum which used Wiser 2.

    First one below is for Fronius.

    Code:
    -- importing HTTP and json packages
    local http = require'ssl.https'
    local json = require('json')
    local ltn12 = require("ltn12")
    
    -- Create table, request json file and store (sink)
    local data = {}
    local r, c , h, s  = http.request {
          url = 'http://<your local IP>/solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&DataCollection=CommonInverterData&DeviceId=1',
          method = 'GET',
          headers = {
            ["Content-Type"] = "application/json"
                  },
                  sink = ltn12.sink.table(data)
                }
    
    --combine table and decode json data.
    local response = table.concat(data)
    local data2 = json.decode(response)
    
    --Select data
    totalenergy = data2.Body.Data.TOTAL_ENERGY.Value
    yearenergy = data2.Body.Data.YEAR_ENERGY.Value
    dayenergy = data2.Body.Data.DAY_ENERGY.Value
    
    --Write to user parameter
    SetUserParam(0, 20, totalenergy)
    SetUserParam(0, 21, yearenergy)
    SetUserParam(0, 22, dayenergy)
    The second is for Tech Sterowniki. This is done through quering the emodul.pl API interface. Please note obvious for secuity reasons <userID>, <module no> and <authorisation code> have been removed. These can be generated by using postman application and following the API guide attached...

    Code:
    -- importing HTTP and json packages
    local http = require'ssl.https'
    local json = require('json')
    local ltn12 = require("ltn12")
    
    -- Create table, request http, authorisation code in header and sink into table.
    
    local data = {}
    local r, c , h, s  = http.request {
          url = 'https://emodul.eu/api/v1/users/<userID>/modules/<module no>',
          method = 'GET',
          headers = {
            ["Authorization"] = "Bearer <Authorisation code>",
                     ["Content-Type"] = "application/json"
                  },
                  sink = ltn12.sink.table(data)
                 }
    
    --combine table and decode json data.
    local response = table.concat(data)
    local data3 = json.decode(response)
    
    --Transfer data to Cbus User parameter - Salon[1}, Kuchnia[2], Wiatrołap[3], Pokój Dół[4], Korytarz Dół[5], Toleta[6], Pomp. Gosp[7], Garaż[8]
    --Create decimal by divide by ten as number comes without it.
    
    salontemp = data3.zones.elements[1].zone.currentTemperature / 10
    kuchniatemp = data3.zones.elements[2].zone.currentTemperature /10
    wiatrolaptemp = data3.zones.elements[3].zone.currentTemperature /10
    pokojdoltemp = data3.zones.elements[4].zone.currentTemperature /10
    korytarztemp = data3.zones.elements[5].zone.currentTemperature /10
    toletatemp = data3.zones.elements[6].zone.currentTemperature /10
    pompgosptemp = data3.zones.elements[7].zone.currentTemperature /10
    garaztemp = data3.zones.elements[8].zone.currentTemperature /10
    
    --Transfer data to Cbus User parameter - Sypalnia[9], Łazienka Z&G[10], Pokój Gier[11], Spare[12], Korytarz Gora[13], Łazienka[14],  Pokój Oskar[15], Pokój Daniel [16]
    
    sypalniatemp = data3.zones.elements[9].zone.currentTemperature /10
    lazienkazgtemp = data3.zones.elements[10].zone.currentTemperature /10
    pokojgiertemp = data3.zones.elements[11].zone.currentTemperature /10
    --Sparetemp = data3.zones.elements[12].zone.currentTemperature /10
    korytarzgoratemp = data3.zones.elements[13].zone.currentTemperature /10
    lazienkatemp = data3.zones.elements[14].zone.currentTemperature /10
    pokojoskartemp = data3.zones.elements[15].zone.currentTemperature /10
    pokojdanieltemp = data3.zones.elements[16].zone.currentTemperature /10
    
    -- send to userparameter
    SetUserParam(0, 4, salontemp)
    SetUserParam(0, 5, kuchniatemp)
    SetUserParam(0, 6, wiatrolaptemp)
    SetUserParam(0, 7, pokojdoltemp)
    SetUserParam(0, 8, korytarztemp)
    SetUserParam(0, 9, toletatemp)
    SetUserParam(0, 10, pompgosptemp)
    SetUserParam(0, 11, garaztemp)
    SetUserParam(0, 19, sypalniatemp)
    SetUserParam(0, 12, lazienkazgtemp)
    SetUserParam(0, 13, pokojgiertemp)
    SetUserParam(0, 14, Sparetemp)
    SetUserParam(0, 15, korytarzgoratemp)
    SetUserParam(0, 16, lazienkatemp)
    SetUserParam(0, 17, pokojoskartemp)
    SetUserParam(0, 18, pokojdanieltemp)
    
    An finally SONOS. Once again I used node js on windows setup and run the following code to give me the API.
    https://github.com/jishi/node-sonos-http-api

    Then on the SHAC side I have the following code examples. I use an EDLT to trigger the code.

    Play/Pause
    Code:
    playpause = event.getvalue()
    if (playpause == 255) then
      http = require'socket.http'
      body = http.request("http://<Windows PC IP>:5005/Bedroom/play")
    else
      http = require'socket.http'
      body = http.request("http://<Windows PC IP>:5005/Bedroom/pause")
    end
    
    -- Read state of amp, extract volume level, convert to number and send to cbus state
    http = require'socket.http'
    body = http.request("http://<Windows PC IP>:5005/Bedroom/state")
    volumestring = string.match(body,'volume":(%d+)')
    volumenumber = tonumber(volumestring)
    volumelevel = CBusPctToLevel(volumenumber)
    SetCBusLevel(0, 56, 134, volumelevel,0)
    Volume Control

    Code:
    --send volume level
    sendvolumenum = event.getvalue()
    sendvolumep = CBusLevelToPct(sendvolumenum)
    sendvolumestring = tostring(sendvolumep)
    http = require'socket.http'
    body = http.request("http://<Windows PC IP>:5005/Bedroom/volume/"..sendvolumestring)
    log(body)
    Start radio, update volume state and play if stopped.

    Code:
    --change to magic
    http = require'socket.http'
    body = http.request("http://<Windows PC IP>:5005/ Bedroom/Favourite/Magic")
    
    -- Read state of amp, extract volume elevel, convert to number and send to cbus state
    http = require'socket.http'
    body = http.request("http://<Windows PC IP>:5005/Bedroom/state")
    volumestring = string.match(body,'volume":(%d+)')
    volumenumber = tonumber(volumestring)
    volumelevel = CBusPctToLevel(volumenumber)
    SetCBusLevel(0, 56, 134, volumelevel,0)
    
    -- Read state of amp, extract play state and set to correct state
    playstate = string.match(body,'playbackState":"(%u%u%u%u%u%u%u)')
    if  ('STOPPED' == playstate) then
        SetCBusState(0, 56, 133, false)
    elseif ('PLAYING' == playstate) then
      SetCBusState(0, 56, 133, true)
    end
    Hoping this will help someone. Happy Lua coding.
     

    Attached Files:

    gte242, Oct 17, 2021
    #1
    Shiney2512 likes this.
  2. gte242

    gte242

    Joined:
    Apr 7, 2017
    Messages:
    31
    Likes Received:
    5
    Location:
    Poland
    Updated my Sonos scipt to run as resident scipt which can extract with ease the status Json file which sonos requires and info can be sent to EDLT's
    Code:
    -- importing HTTP and json packages
    local http = require'ssl.https'
    local json = require('json')
    local ltn12 = require("ltn12")
    
    -- **************************************************************************
    -- ************************KITCHEN SONOS DATA EXTRACT************************
    -- **************************************************************************
    
    -- Create table, request json file and store (sink)
    local data = {}
    local r, c , h, s  = http.request {
          url = 'http://YOUR IP:5005/kitchen/state',
          method = 'GET',
          headers = {
            ["Content-Type"] = "application/json"
                  },
                  sink = ltn12.sink.table(data)
                }
    
    --combine table and decode json data.
    local response = table.concat(data)
    local data2 = json.decode(response)
    
    --Select data
    kitchenvolume = data2.volume
    kitchenmute = data2.mute
    kitchenplayback = data2.playbackState
    kitchenartist = data2.currentTrack.artist
    kitchentitle = data2.currentTrack.title
    kitchentimelapse = data2.elapsedTimeFormatted
    
    --Send playing information (max 14 charactors on EDLT)
    
    if kitchenartist == nil or kitchenartist == "" then
      SetCBusUnicodeLabel(0, 56, 135, 'English', 'Variant1', "")
    else
      if kitchenartist ~= changeartist then
            kitchenartist2 = string.sub(kitchenartist, 1, 14)
            SetCBusUnicodeLabel(0, 56, 135, 'English', 'Variant1', kitchenartist2)
        changeartist = kitchenartist
      end
    end
    
    --Update volume on EDLT
    cbuskitchenlevel = GetCBusLevel(0, 56, 138)
    kitchenlevel = CBusPctToLevel(kitchenvolume)
    if kitchenlevel ~= cbuskitchenlevel then
     SetCBusLevel(0, 56, 138, kitchenlevel,0)
    end
    
    --Update play state group
    cbuskitchenplay = GetCBusLevel(0, 56, 137)
    if kitchenplayback == "PLAYING" and cbuskitchenplay == 0 then
        SetCBusLevel(0, 56, 137, 255, 0)
    elseif kitchenplayback == "STOPPED" and cbuskitchenplay == 255 then
      SetCBusLevel(0, 56, 137, 0, 0)
    elseif kitchenplayback == "PAUSED_PLAYBACK" and cbuskitchenplay == 255 then
      SetCBusLevel(0, 56, 137, 0, 0)
    end
    
    --Write to user parameter for SHAC
    SetUserParam(0, 63, kitchenartist)
    SetUserParam(0, 64, kitchentitle)
    SetUserParam(0, 65, kitchentimelapse)
     
    gte242, Nov 8, 2022
    #2
    SgrAystar likes this.
  3. gte242

    Colin Moderator

    Joined:
    Aug 3, 2004
    Messages:
    120
    Likes Received:
    23
    Location:
    Australia
    For Sonos, Why do you not use the Multiroom Audio Application in place of Lighting,

    Then in eDLT you can use the Audio Functions for Volume and Commands, and if you have multiple Sonos Zones you can use the Zone Numbering in C-Bus to easily separate,

    You could make a very adaptable re-usable bit of code if you chose this path over Lighting Applications,
    Effectively just assigning each sonos unit to a MRA Zone
     
    Colin, Nov 11, 2022
    #3
  4. gte242

    Ks04

    Joined:
    Dec 28, 2019
    Messages:
    107
    Likes Received:
    9
    I'm working on this as we speak!
     
    Ks04, Nov 17, 2022
    #4
  5. gte242

    gte242

    Joined:
    Apr 7, 2017
    Messages:
    31
    Likes Received:
    5
    Location:
    Poland
    Colin

    Thanks for your input. Hadn't thought about that route. Wrote it off because it didn't give me all the functionality that I required through the eDLT. However after you comment and some time over Xmas to have a play came up with this solution:
    upload_2022-12-28_20-35-16.png

    1. Dynamic 1 shows song title and button allows pause/play
    2. Dynamic 2 shows artist and button allows to skip to next track.
    3. Volume acts as it states.
    4. Source button allows to scroll through radio stations. ( 7 in total - 0-6 as field is limted to this)

    Additional Bass and Treble controls can be added to EDLT through the MRA zonecontrol but I don't show in screen shot however code is in the scripts for them.

    Objects were created in application 205:
    upload_2022-12-28_20-49-42.png
    205/0/0 - Volume
    205/0/2 - Bass
    205/0/3 - Treble
    205/0/5 - Source number linked to radio station index in coding. Use this as retentative memory.
    205/0/6 - Dynamic 1 - Artist
    205/0/7 - Dynamic 2 - Title
    205/0/8 - Source descrptor - Radio station desciption
    205/0/10 - MRA Command - Use this to control the SONOS details below

    205/0/1 - Balance - I did not use this because no functionality in eDLT and SONOS does not send this on the state request json.
    205/0/4 - Mute - I did not use this as it is the same as pausing effectively. Pausing gives more functionality as it stops the song.

    So trying match systems up I used the following commands out of the MRA command list as these are the ones which can be sent from eDLT through application 205.
    upload_2022-12-28_21-2-42.png

    The coding is still being tested and debugged at this stage. It is split into 2 scripts a
    1. Resident for updates from the Sonos system to C-Bus
    2. Event triggered for the button presses on the eDLT/ SHAC webpage.
    IP addresses have been deleted for obvious reasons :)

    1. Resident code - I still had to use a group (retentative memory) for play/pause tthis allows SONOS and the MRA logic to sync.

    Code:
    -- **************************************************************************
    -- ************************LOUNGE SONOS DATA EXTRACT************************
    -- **************************************************************************
    -- Create table, request json file and store (sink)
    local data5 = {}
    local r, c , h, s  = http.request {
          url = 'http://<YOUR IP>:5005/Lounge/state',
          method = 'GET',
          headers = {
            ["Content-Type"] = "application/json"
                  },
                  sink = ltn12.sink.table(data5)
                }
    
    --combine table and decode json data.
    local response3 = table.concat(data5)
    local data6 = json.decode(response3)
    
    --Select data from table JSON response.
    loungevolume = data6.volume
    loungebass = data6.equalizer.bass
    loungetreble = data6.equalizer.treble
    loungenmute = data6.mute
    loungeplayback = data6.playbackState
    loungeartist = data6.currentTrack.artist
    loungetitle = data6.currentTrack.title
    loungetimelapse = data6.elapsedTimeFormatted
    
    --Update play state group
    cbusloungeplay = GetCBusLevel(0, 56, 128)
    
    if loungeplayback == "PLAYING" and cbusloungeplay == 0 then
        SetCBusLevel(0, 56, 128, 255, 0)
    elseif loungeplayback == "STOPPED" and cbusloungeplay == 255 then
      SetCBusLevel(0, 56, 128, 0, 0)
    elseif loungeplayback == "PAUSED_PLAYBACK" and cbusloungeplay == 255 then
      SetCBusLevel(0, 56, 128, 0, 0)
    end
    
    -- Send to network
    
    loungevolume255 = CBusPctToLevel(loungevolume)
    CBusMRASetObjectValue(0, 0 , 0, loungevolume255)
    loungebass255 = CBusPctToLevel(loungebass)
    CBusMRASetObjectValue(0, 0 , 2, loungebass255)
    loungetreble255 = CBusPctToLevel(loungetreble)
    CBusMRASetObjectValue(0, 0 , 3, loungetreble255)
    CBusMRASetObjectValue(0, 0 , 6, loungeartist)
    CBusMRASetObjectValue(0, 0 , 7, loungetitle)
    2. Event Trigger - looks at group 205/0/0/10 - MRA command and the change in text dictates the action. This is not as good as I hoped but it was working after my first testing. Will condense and improve in due course.Please note your radio stations need to stored in your favourites an dthe decription to match in order for it to work.

    Code:
    --Get MRA command
    MRAcommand = event.getvalue()
    log(MRAcommand)
    
    if MRAcommand == "Status Request" then
         -- do nothing handshake as command executed
     
    elseif MRAcommand == "Dynamic1" then
      playpause = GetCBusLevel(0, 56, 128)
      log(playpause)
        if (playpause == 0) then
           http = require'socket.http'
          body = http.request("http://<YOUR IP>:5005/lounge/play")
        CBusMRASetObjectValue(0, 0 , 10, {value='Status Request'})
        else
              http = require'socket.http'
          body = http.request("http://<YOUR IP>:5005/lounge/pause")
        CBusMRASetObjectValue(0, 0 , 10, {value='Status Request'})
        end
     
    elseif MRAcommand == "Dynamic2" then
           http = require'socket.http'
          body = http.request("http://<YOUR IP>:5005/Lounge/next")
        CBusMRASetObjectValue(0, 0 , 10, {value='Status Request'})
    
    elseif MRAcommand == "Next Feed" then
          r = CBusMRAGetObjectValue(0, 0, 5)
            radiostation = r["value"]
            if radiostation == 6 then
            radiostation2 = 0
            CBusMRASetObjectValue(0, 0 , 5, 0)
        else
            radiostation2 = radiostation + 1
        end
          log(radiostation)
          if radiostation2 == 1 then
              http = require'socket.http'
              body = http.request("http://<YOUR IP>:5005/Lounge/Favourite/RMF MAXXX")
              CBusMRASetObjectValue(0, 0 , 8, {value='RMF MAXX'})
            elseif radiostation2 == 2 then
                 http = require'socket.http'
              body = http.request("http://<YOUR IP>:5005/Lounge/Favourite/Radio Zlote Przeboje")
                  CBusMRASetObjectValue(0, 0 , 8, {value='Zlote Przeboje'})
            elseif radiostation2 == 3 then
                http = require'socket.http'
              body = http.request("http://<YOUR IP>:5005/Lounge/Favourite/Capital London")
              CBusMRASetObjectValue(0, 0 , 8, {value='Capital London'})
          elseif radiostation2 == 4 then
                http = require'socket.http'
              body = http.request("http://<YOUR IP>:5005/Lounge/Favourite/Heart UK")
              CBusMRASetObjectValue(0, 0 , 8, {value='Heart UK'})
            elseif radiostation2 == 5 then
                http = require'socket.http'
              body = http.request("http://<YOUR IP>:5005/Lounge/Favourite/Radio Zet Kids")
              CBusMRASetObjectValue(0, 0 , 8, {value='Radio Zet Kids'})
            elseif radiostation2 == 6 then
                http = require'socket.http'
              body = http.request("http://<YOUR IP>:5005/Lounge/Favourite/SuperNova")
              CBusMRASetObjectValue(0, 0 , 8, {value='SuperNova'})
          end
          log (radiostation2)
          CBusMRASetObjectValue(0, 0 , 5, radiostation2)
          CBusMRASetObjectValue(0, 0 , 10, {value='Status Request'})
     
    elseif MRAcommand  == "Previous Feed" then
          r = CBusMRAGetObjectValue(0, 0, 5)
            radiostation = r["value"]
          if radiostation == 0 then
            radiostation2 =  6
            else
            radiostation2 = radiostation - 1
        end
          log (radiostation2)
          if radiostation2 == 1 then
              http = require'socket.http'
              body = http.request("http://10.0.0.13:5005/Lounge/Favourite/RMF MAXXX")
              CBusMRASetObjectValue(0, 0 , 8, {value='RMF MAXX'})
            elseif radiostation2 == 2 then
                 http = require'socket.http'
              body = http.request("http://10.0.0.13:5005/Lounge/Favourite/Radio Zlote Przeboje")
                  CBusMRASetObjectValue(0, 0 , 8, {value='Zlote Przeboje'})
            elseif radiostation2 == 3 then
                http = require'socket.http'
              body = http.request("http://10.0.0.13:5005/Lounge/Favourite/Capital London")
              CBusMRASetObjectValue(0, 0 , 8, {value='Capital London'})
          elseif radiostation2 == 4 then
                http = require'socket.http'
              body = http.request("http://10.0.0.13:5005/Lounge/Favourite/Heart UK")
              CBusMRASetObjectValue(0, 0 , 8, {value='Heart UK'})
            elseif radiostation2 == 5 then
                http = require'socket.http'
              body = http.request("http://10.0.0.13:5005/Lounge/Favourite/Radio Zet Kids")
              CBusMRASetObjectValue(0, 0 , 8, {value='Radio Zet Kids'})
            elseif radiostation2 == 6 then
                http = require'socket.http'
              body = http.request("http://10.0.0.13:5005/Lounge/Favourite/SuperNova")
               CBusMRASetObjectValue(0, 0 , 8, {value='SuperNova'})
        end
          log (radiostation2)
          CBusMRASetObjectValue(0, 0 , 5, radiostation2)
          CBusMRASetObjectValue(0, 0 , 10, {value='Status Request'})
         
    end
    
    For me the advanatages of doing it this way were:

    1. Reduced the amount of groups used in lighting.
    2. Didn't have to right additional code to stop the resident code from flooding the network every 10 seconds.
    3. Easier to set up on the eDLT. Select the zone number and away you go.
    4. For radio selection I was using scene functionality to select the radio station on the eDLT. I can now use this for lighting control.
    5. Utilise one line on eDLT to do two things - play/pause and show title.
    6. Volume control - removes the on/off buttons on the shac http interface. "On" would put it up to 100%.

    Saying that some disdvantages were
    1. Volume control - limited to increments of 7.8% on the volume control where as through the lighting application you can set your nudge value.
    2. Shame the MTC objects (application 192) can't be selected on the eDLT as this would make the coding even easier.
    upload_2022-12-28_21-32-37.png

    3. Run out of commands on eDLT to use - example grouping zones, shuffle, time etc can not be included through this way. This would have to stay on lighting application.
    4. Stuggling to find away of allowing the commands to work on the SHAC HTTP interface for the dynamic fields and slecting the radio station. Curently my buttons don't work on selection of the group. for next, play, pause and source select. Work in progress :)

    upload_2022-12-28_22-14-21.png


    Hope this helps everyone.

    Regards

    Gareth
     

    Attached Files:

    Last edited: Dec 28, 2022
    gte242, Dec 28, 2022
    #5
  6. gte242

    gte242

    Joined:
    Apr 7, 2017
    Messages:
    31
    Likes Received:
    5
    Location:
    Poland
    Sorted the last bit select MRA Command then select action in visualisation parameters.

    upload_2022-12-30_21-1-54.png

    Will update code in week or so.
     
    gte242, Dec 30, 2022
    #6
  7. gte242

    gte242

    Joined:
    Apr 7, 2017
    Messages:
    31
    Likes Received:
    5
    Location:
    Poland
    A week or so turned into a month after some testing by my kids and wife.

    Final code using MRA to control Sonos
    So the code sits in four scripts. 1 resident and 3 event based.

    1. Sonos data Extract (Resident).
    2. Volume control.
    3. MRA Command.
    4. Set radio from MRA source. (this has been split out to allow user to scroll through radio stations before being set - I have allowed 2 seconds which works for me).

    1. Sonos data extract (duplicate for additional zones)
    Code:
    -- importing HTTP and json packages
    local http = require'ssl.https'
    local json = require('json')
    local ltn12 = require("ltn12")
    
    ip = "<your ip>"
    MRAzonekitchen = 1
    
    -- **************************************************************************
    -- ************************KITCHEN SONOS DATA EXTRACT MRA Zone 2 (1)*********
    -- **************************************************************************
    
    -- Create table, request json file and store (sink)
    local data1 = {}
    local r, c , h, s  = http.request {
          url = 'http://'..ip..':5005/kitchen/state',
          method = 'GET',
          headers = {
            ["Content-Type"] = "application/json"
                  },
                  sink = ltn12.sink.table(data1)
                }
    
    --combine table and decode json data.
    local response1 = table.concat(data1)
    local data2 = json.decode(response1)
    
    --Select data
    kitchenvolume = data2.volume
    kitchenbass = data2.equalizer.bass
    kitchentreble = data2.equalizer.treble
    kitchenmute = data2.mute
    kitchenplayback = data2.playbackState
    kitchenartist = data2.currentTrack.artist
    kitchentitle = data2.currentTrack.title
    kitchentimelapse = data2.elapsedTimeFormatted
    
    --Update play state group
    cbuskitchenplay = GetCBusLevel(0, 56, 137)
    if kitchenplayback == "PLAYING" and cbuskitchenplay == 0 then
        SetCBusLevel(0, 56, 137, 255, 0)
    elseif kitchenplayback == "STOPPED" and cbuskitchenplay == 255 then
      SetCBusLevel(0, 56, 137, 0, 0)
    elseif kitchenplayback == "PAUSED_PLAYBACK" and cbuskitchenplay == 255 then
      SetCBusLevel(0, 56, 137, 0, 0)
    end
    
    -- Send to network
    if kitchenvolume ~= nil then
      kitchenvolume255 = CBusPctToLevel(kitchenvolume)
      CBusMRASetObjectValue(0, MRAzonekitchen, 0, kitchenvolume255)
    end
    if kitchenbass ~= nil then
        kitchenbass255 = CBusPctToLevel(kitchenbass)
        CBusMRASetObjectValue(0, MRAzonekitchen, 2, kitchenbass255)
    end
    if kitchentreble ~= nil then
        kitchentreble255 = CBusPctToLevel(kitchentreble)
        CBusMRASetObjectValue(0, MRAzonekitchen, 3, kitchentreble255)
    end
    if kitchentitle ~= nil then
         CBusMRASetObjectValue(0, MRAzonekitchen, 7, kitchentitle)
    end
    if kitchenartist ~= nil then
         CBusMRASetObjectValue(0, MRAzonekitchen, 6, kitchenartist)
    end
    2. Volume Control (sets the volume on button press) triggered from group HOME/MRA/Zone 1/Volume (0/205/1/0)
    Code:
    --send volume level
    ipaddress = "<your IP>"
    
    sendvolumenum = event.getvalue()
    sendvolumep = CBusLevelToPct(sendvolumenum)
    sendvolumestring = tostring(sendvolumep)
    http = require'socket.http'
    body = http.request("http://"..ipaddress..":5005/Kitchen/volume/"..sendvolumestring)
    3. MRA command triggered from HOME/MRA/Zone 1/MRA Command (0/205/1/10)
    Code:
    --Get MRA command
    MRAcommand = event.getvalue()
    
    -- Set server ip (PC/Rasberry pie running sonos api), zone and pause /play group (pp).
    -- Radio 1-7 - Set text for eDLT
    -- Zone must be the same spelling as in sonos app,
    -- All MRA groups need to be added in SHAC (application 205).
    -- Play/Pause  group needs to be adding to lighting application and added below under pp.
    -- MRA zone is assigned to a Sonos zone - Don't forget Zone 1 eDLT is Zone 0 in SHAC, zone2 eDLT is Zone 1 SHAC and so on.
    
    ip ="<your ip>"
    zone = "Kitchen"
    MRAzone = 1
    pp = 137
    radio1 = "KISS"
    radio2 = "RMF MAXXX"
    radio3 = "Zlote Przeboje"
    radio4 = "Capital London"
    radio5 = "Heart"
    radio6 = "Zet Kids"
    radio7 = "SuperNova"
    
    if MRAcommand == "Status Request" then
         -- do nothing handshake as command executed
    
    -- Dynamic1 = Play/Pause Group 137 used to toggle.
    elseif MRAcommand == "Dynamic1" then
      playpause = GetCBusLevel(0, 56, pp)
          if (playpause == 0) then
               http = require'socket.http'
              body = http.request("http://"..ip..":5005/"..zone.."/play")
            SetCBusLevel(0, 56, pp, 255, 0)
            CBusMRASetObjectValue(0, MRAzone , 10, {value='Status Request'})
            else
          http = require'socket.http'
              body = http.request("http://"..ip..":5005/"..zone.."/pause")
            SetCBusLevel(0, 56, pp, 0, 0)
            CBusMRASetObjectValue(0, MRAzone , 10, {value='Status Request'})
            end
    
    -- Dynamic 2 = Next Track
    elseif MRAcommand == "Dynamic2" then
          http = require'socket.http'
          body = http.request("http://"..ip..":5005/"..zone.."/next")
        CBusMRASetObjectValue(0, MRAzone , 10, {value='Status Request'})
    
    -- Next feed = Next/Previous radio station
    else
          if MRAcommand == "Next Feed" then
              r = CBusMRAGetObjectValue(0, MRAzone , 5)
                radiostation = r["value"]
                if radiostation == 6 then
                radiostation2 = 0
                CBusMRASetObjectValue(0, MRAzone , 5, 0)
            else
                radiostation2 = radiostation + 1
            end
    
          elseif MRAcommand  == "Previous Feed" then
              r = CBusMRAGetObjectValue(0, MRAzone, 5)
                radiostation = r["value"]
              if radiostation == 0 then
                radiostation2 =  6
                else
                radiostation2 = radiostation - 1
            end
         end
        CBusMRASetObjectValue(0, MRAzone , 5, radiostation2)   -- Triigers radio station HTTP request
        CBusMRASetObjectValue(0, MRAzone , 10, {value='Status Request'})
     
    -- Set display to show radio station slection. Set here for quick update to screen
        if radiostation2 == 0 then
             CBusMRASetObjectValue(0, MRAzone , 8, {value=radio1})
        elseif radiostation2 == 1 then
          CBusMRASetObjectValue(0, MRAzone , 8, {value=radio2})
        elseif radiostation2 == 2 then
            CBusMRASetObjectValue(0, MRAzone , 8, {value=radio3})
        elseif radiostation2 == 3 then
            CBusMRASetObjectValue(0, MRAzone , 8, {value=radio4})
        elseif radiostation2 == 4 then
            CBusMRASetObjectValue(0, MRAzone , 8, {value=radio5})
        elseif radiostation2 == 5 then
            CBusMRASetObjectValue(0, MRAzone , 8, {value=radio6})
        elseif radiostation2 == 6 then
            CBusMRASetObjectValue(0, MRAzone , 8, {value=radio7})
        end    
    end
    4. Set Radio from MRA source
    Code:
    -- set radio station based on MRA Source number value
    -- radio must be in favourites and spelling must be exact on this script
    -- change "Favourite" to "Playlist" and you can add a playlist
    
    ip ="<your ip>"
    zone = "Kitchen"
    MRAzone = 1
    radio1 = "KISS"
    radio2 = "RMF MAXXX"
    radio3 = "Radio Zlote Przeboje"
    radio4 = "Capital London"
    radio5 = "Heart UK"
    radio6 = "Radio Zet Kids"
    radio7 = "SuperNova"
    
    -- allows time for user to skip to the righ radio station
    sleep(2)
    r = CBusMRAGetObjectValue(0, MRAzone, 5)
    MRAsource = r["value"]
    
    if MRAsource == 0 then
           http = require'socket.http'
        body = http.request("http://"..ip..":5005/"..zone.."/Favourite/"..radio1)
    elseif MRAsource == 1 then
      http = require'socket.http'
        body = http.request("http://"..ip..":5005/"..zone.."/Favourite/"..radio2)
    elseif MRAsource == 2 then
        http = require'socket.http'
        body = http.request("http://"..ip..":5005/"..zone.."/Favourite/"..radio3)
    elseif MRAsource == 3 then
        http = require'socket.http'
        body = http.request("http://"..ip..":5005/"..zone.."/Favourite/"..radio4)
    elseif MRAsource == 4 then
        http = require'socket.http'
        body = http.request("http://"..ip..":5005/"..zone.."/Favourite/"..radio5)
    elseif MRAsource == 5 then
        http = require'socket.http'
        body = http.request("http://"..ip..":5005/"..zone.."/Favourite/"..radio6)
    elseif MRAsource == 6 then
        http = require'socket.http'
        body = http.request("http://"..ip..":5005/"..zone.."/Favourite/"..radio7)
    end     
    I have additional lighting groups to group Sonos speakers etc.

    eDLT has no changes to it from original setup at the top of this thread and same for SHAC page.

    Good luck everyone. If you find this useful then please like my posts to gain how many users use the code and any issue you may have. Thanks.

    Gareth
     
    Last edited: Feb 5, 2023
    gte242, Feb 5, 2023
    #7
    MadMal likes this.
  8. gte242

    MadMal

    Joined:
    Dec 16, 2009
    Messages:
    75
    Likes Received:
    1
    Location:
    Perth, WA
    Hi Gareth,

    How about this for an improvement on your Data Extract routine .. allows you to have just one scheduled event that is configured to get all the updates ...

    Should be reasonably obvious, add a new ZoneX variable for each zone with name, MRA Zone # and GA.

    Then ensure the Zones variable has all the ZoneX variables.

    Code:
    -- importing HTTP and json packages
    local http = require'ssl.https'
    local json = require('json')
    local ltn12 = require("ltn12")
    
    -- IP Address of SONOS API Server
    ip = "192.168.0.136"
    
    local Zone1 = {Name = "Study", MRAZone = 11, GA = 201}
    local Zone2 = {Name = "White Dining", MRAZone = 12, GA = 202}
    local Zone3 = {Name = "Master Bedroom", MRAZone = 13, GA = 203}
    local Zone4 = {Name = "Games Room", MRAZone = 14, GA = 204}
    
    local Zones = { Zone1, Zone2, Zone3, Zone4 }
    
    for _, Zone in ipairs(Zones) do
      -- Create table, request json file and store (sink)
      local data1 = {}
      local r, c , h, s  = http.request {
          url = 'http://'..ip..':5005/'..Zone.Name..'/state',
          method = 'GET',
          headers = {
            ["Content-Type"] = "application/json"
                  },
                  sink = ltn12.sink.table(data1)
                }
    
      --combine table and decode json data.
      local response1 = table.concat(data1)
      local data2 = json.decode(response1)
    
      --Select data
      zone_volume = data2.volume
      zone_bass = data2.equalizer.bass
      zone_treble = data2.equalizer.treble
      zone_mute = data2.mute
      zone_playback = data2.playbackState
      zone_artist = data2.currentTrack.artist
      zone_title = data2.currentTrack.title
      zone_timelapse = data2.elapsedTimeFormatted
    
      --Update play state group
      cbuszone_play = FIT_GetCBUSDefZero(0, 56, Zone.GA)
      if zone_playback == "PLAYING" and cbuszone_play == 0 then
        SetCBusLevel(0, 56, Zone.GA, 255, 0)
      elseif zone_playback == "STOPPED" and cbuszone_play == 255 then
        SetCBusLevel(0, 56, Zone.GA, 0, 0)
      elseif zone_playback == "PAUSED_PLAYBACK" and cbuszone_play == 255 then
        SetCBusLevel(0, 56, Zone.GA, 0, 0)
      end
    
      -- Send to network
      if zone_volume ~= nil then
        zone_volume255 = CBusPctToLevel(zone_volume)
        CBusMRASetObjectValue(0, Zone.MRAZone, 0, zone_volume255)
      end
      if zone_bass ~= nil then
        zone_bass255 = CBusPctToLevel(zone_bass)
        CBusMRASetObjectValue(0, Zone.MRAZone, 2, zone_bass255)
      end
      if zone_treble ~= nil then
        zone_treble255 = CBusPctToLevel(zone_treble)
        CBusMRASetObjectValue(0, Zone.MRAZone, 3, zone_treble255)
      end
      if zone_title ~= nil then
         CBusMRASetObjectValue(0, Zone.MRAZone, 7, zone_title)
      end
      if zone_artist ~= nil then
         CBusMRASetObjectValue(0, Zone.MRAZone, 6, zone_artist)
      end
    end
    
    Cheers,

    Mal.
     
    MadMal, Jul 20, 2023
    #8
Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.