Logic Machine Forum
Panasonic Viera TV IP control - Printable Version

+- Logic Machine 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: Panasonic Viera TV IP control (/showthread.php?tid=232)

Pages: 1 2


Panasonic Viera TV IP control - jetsetter - 29.02.2016

Dear friends,

According to this site, it seems that there a possibility to control Panasonic Viera TVs through IP commands.
Since my LUA knowledge is very starter level, can anyone give me a quick example how to send a simple "power off" command through LUA scripting so I can try to move it forward with the rest of the commands?

Thank you all in advance.


RE: Panasonic Viera TV IP control - admin - 10.03.2016

Try these functions, they are untested so might not work at all. You have to parse any XML response manually
Code:
function request(host, url, urn, action, args)
  local body, http, ltn12, sink, res, err

  ltn12 = require('ltn12')
  http = require('socket.http')
  body = [[<?xml version="1.0" encoding="utf-8"?>
  <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body><u:]] .. action .. [[ xmlns:u="urn:]] .. urn .. [[">]] .. args .. [[</u:]] .. action .. [[></s:Body>
  </s:Envelope>]]

  sink = {}

  res, err = http.request({
    url = 'http://' .. host .. ':55000/' .. url,
    method = 'POST',
    headers = {
      ['soapaction'] = '"urn:' .. urn .. '#' .. action .. '"',
      ['Content-Length'] = #body,
    },
    sink = ltn12.sink.table(sink),
    source = ltn12.source.string(body),
  })

  if res then
    return table.concat(sink)
  else
    return nil, err
  end
end

function sendkey(host, code)
  local args = '<X_KeyEvent>' .. code .. '</X_KeyEvent>'
  return request(host, 'nrc/control_0', 'panasonic-com:service:p00NetworkControl:1', 'X_SendKey', args)
end

function getmute(host)
  return request(host, 'dmr/control_0', 'schemas-upnp-org:service:RenderingControl:1', 'GetMute', '<InstanceID>0</InstanceID><Channel>Master</Channel>')
end

IP = '192.168.1.2'
log(getmute(IP))



RE: Panasonic Viera TV IP control - jetsetter - 15.03.2016

Thank you very much.
I will give it a try and report back.

I have successfully add a user library with those functions and then have send a "NRC_POWER-ONOFF" code to the TV and it worked!! Thank you very much! Smile

I will now try to complete the rest of the commands and - if possible - to create a simple panel to simulate the remote control via the LM visualization!

One last question, since this TV is not powered via a bus-controlable outlet, and also does not support any wake-on-lan functionality, in order to update the TV's "ON" status after I turn it off via the above IP command, is there a way to periodicaly check if a specific IP is alive (via ping?) or a specific domain name is alive (it always register to DHCP as "COM-MID1") in my LAN?
Than you in advance.


RE: Panasonic Viera TV IP control - admin - 16.03.2016

You can use this function to check if given TCP port on your device is accepting connections:
Code:
function socketping(ip, port, timeout)
  local sock = require('socket').tcp()
  sock:settimeout(timeout or 2)
  local res, err = sock:connect(ip, port)
  sock:close()
  
  return res, err
end

IP = '192.168.1.10'
PORT = 55000
status = socketping(IP, PORT)



RE: Panasonic Viera TV IP control - jetsetter - 16.03.2016

Just add this to the rest of TV functions and works great too!
Thank you once more for your support.

My next try will be to control another TV that doesn't have native IP control (no LAN port at all). This will be done through HDMI CEC commands that will be send via Kodi (formerly XBMC media player) running on a RPI, through Kodi's JSON RPC API and a relative Kodi add-on, so I will update with the results and more details upon success...

Thanks!


RE: Panasonic Viera TV IP control - jetsetter - 18.03.2016

Just finished successfully also the power-off control of another non-smart TV, through a Kodi media player (running in an RPi) connected to it via HDMI.
If anyone is interested, the code I used is the following:

Code:
require('socket.http')
socket.http.TIMEOUT = 5
data1 = socket.http.request('http://KODI-IP-ADDRESS/jsonrpc?request={"jsonrpc":"2.0","method":"Addons.ExecuteAddon","params":{"addonid":"script.json-cec","params":{"command":"toggle"}},"id":1}')
socket.http.TIMEOUT = 5
data2 = socket.http.request('http://KODI-IP-ADDRESS/jsonrpc?request={"jsonrpc":"2.0","method":"Addons.ExecuteAddon","params":{"addonid":"script.json-cec","params":{"command":"standby"}},"id":1}')

Sends two CEC commands to the TV through HDMI. One is for swapping the TV source to the HDMI of KODI, and the second is for putting the TV to standby (the TV should be on this source in order for the standby command to work).

The KODI add-on used for the above script can be found here:


RE: Panasonic Viera TV IP control - Gadjoken - 15.09.2016

Hello, 
I have a Samsung smart tv and i want to control TV through IP commands.
Have you a script or a app for that?
Thanks.


RE: Panasonic Viera TV IP control - andeug - 10.10.2016

I need for Sony Bravia Android TVs, too!


RE: Panasonic Viera TV IP control - Habib - 08.04.2018

Hi,

I wanted to ask if there was already progress?

I would like to control a SamsungTV with a homeLYnk. Is it with the CEC / HDMI adapter? and does the adapter work with space- / homeLYnk's as well?


RE: Panasonic Viera TV IP control - admin - 08.04.2018

Most TVs have limited CEC support (not all functions are supported). Another issue is that some models cannot be turned on with CEC, only turned off. So you need to do so digging before buying an adapter Smile


RE: Panasonic Viera TV IP control - Habib - 08.04.2018

thx. I've ordered one Chromecast to test it out like describe's in example section.


RE: Panasonic Viera TV IP control - benanderson_475 - 18.01.2020

(10.03.2016, 14:32)admin Wrote: Try these functions, they are untested so might not work at all. You have to parse any XML response manually
Code:
function request(host, url, urn, action, args)
  local body, http, ltn12, sink, res, err

  ltn12 = require('ltn12')
  http = require('socket.http')
  body = [[<?xml version="1.0" encoding="utf-8"?>
  <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body><u:]] .. action .. [[ xmlns:u="urn:]] .. urn .. [[">]] .. args .. [[</u:]] .. action .. [[></s:Body>
  </s:Envelope>]]

  sink = {}

  res, err = http.request({
    url = 'http://' .. host .. ':55000/' .. url,
    method = 'POST',
    headers = {
      ['soapaction'] = '"urn:' .. urn .. '#' .. action .. '"',
      ['Content-Length'] = #body,
    },
    sink = ltn12.sink.table(sink),
    source = ltn12.source.string(body),
  })

  if res then
    return table.concat(sink)
  else
    return nil, err
  end
end

function sendkey(host, code)
  local args = '<X_KeyEvent>' .. code .. '</X_KeyEvent>'
  return request(host, 'nrc/control_0', 'panasonic-com:service:p00NetworkControl:1', 'X_SendKey', args)
end

function getmute(host)
  return request(host, 'dmr/control_0', 'schemas-upnp-org:service:RenderingControl:1', 'GetMute', '<InstanceID>0</InstanceID><Channel>Master</Channel>')
end

IP = '192.168.1.2'
log(getmute(IP))
Now in my region as of 2019 Panasonic have implemented Auth before i can send commands, 
i found this (https://github.com/florianholzapfel/panasonic-viera/issues/9) explaining how it is achieved, but it is python, im not sure where to start to work out how to port it to lua,
I have soap command working to return the iv key my goal is to return the encrypted_payload to send to the tv to make connection.

import binascii
import base64
import hmac, hashlib
from Crypto.Cipher import AES

# Example challenge (which is our IV)
iv = base64.b64decode("mUQdS7/RyJTMsiojPz9i1Q==")

# Get character codes from IV bytes
iv_vals = [ord© for c in iv]

# Initialise key character codes array
key_vals = [0] * 16

# Derive key from IV
i = 0
while i < 16:
    key_vals[i] = ~iv_vals[i + 3] & 0xFF
    key_vals[i + 1] = ~iv_vals[i + 2] & 0xFF
    key_vals[i + 2] = ~iv_vals[i + 1] & 0xFF
    key_vals[i + 3] = ~iv_vals[i] & 0xFF
    i += 4

# Convert our key character codes to bytes
key = ''.join(chr© for c in key_vals)

# Initialise HMAC key mask (taken from libtvconnect.so)
hmac_key_mask_vals = [ord© for c in binascii.unhexlify("15C95AC2B08AA7EB4E228F811E34D04FA54BA7DCAC9879FA8ACDA3FC244F3854")]

# Initialise HMAC key character codes array
hmac_vals = [0] * 32

# Calculate HMAC key using HMAC key mask and IV
i = 0
while i < 32:
    hmac_vals[i] = hmac_key_mask_vals[i] ^ iv_vals[(i + 2) & 0xF]
    hmac_vals[i + 1] = hmac_key_mask_vals[i + 1] ^ iv_vals[(i + 3) & 0xF]
    hmac_vals[i + 2] = hmac_key_mask_vals[i + 2] ^ iv_vals[i & 0xF]
    hmac_vals[i + 3] = hmac_key_mask_vals[i + 3] ^ iv_vals[(i + 1) & 0xF]
    i += 4

# Convert our HMAC key character codes to bytes
hmac_key = ''.join(chr© for c in hmac_vals)

# This is our plaintext SOAP argument for the pin code shown on the TV
authinfo = "<X_PinCode>4410</X_PinCode>"

# First 12 bytes are randomised, let's just set them to 0 because it doesn't matter
payload = "000000000000"

# The next 4 bytes contain the plaintext (SOAP arg) length in big endian
n = len(authinfo)
payload += chr(n >> 24)
payload += chr((n >> 16) & 0xFF)
payload += chr((n >> 8) & 0xFF)
payload += chr(n & 0xFF)

# Now we concatenate our payload, which is starting at byte 17 of the payload
payload += authinfo

# Let's encrypt it with AES-CBC! We need to make sure we pad it to a multiple of 16 bytes beforehand
aes = AES.new(key, AES.MODE_CBC, iv)
ciphertext = aes.encrypt(pad(payload))

# Calculate the HMAC-SHA-256 signature of our encrypted payload
sig = hmac.new(hmac_key, ciphertext, hashlib.sha256).digest()

# Concatenate the HMAC signature to the encrypted payload and base64 encode it, and we're done!
encrypted_payload = base64.b64encode(ciphertext + sig)



RE: Panasonic Viera TV IP control - admin - 20.01.2020

For AES functions use library in this post: https://forum.logicmachine.net/showthread.php?tid=2057
encdec libary supports following HMAC functions:
  • hmacmd5
  • hmacsha1
  • hmacsha256
  • hmacsha384
  • hmacsha512
function arguments:
  1. data
  2. key
  3. raw (set to true to get raw binary hmac value, hex otherwise)



RE: Panasonic Viera TV IP control - benanderson_475 - 14.04.2020

(20.01.2020, 08:32)admin Wrote: For AES functions use library in this post: https://forum.logicmachine.net/showthread.php?tid=2057

for Aes functions,  what format can the key and hash need to be in ? string, table, byte array? etc,

i have the below it runs with no err but result not correct i think key an iv need to be in a different format?

key = 'u�НJ�z�6���Y+�'
iv = 'b/�~��ss��'
payload = 'some data'

local aes, err = aes:new(key, nil, aes.cipher(256, 'cbc'), iv, nil, 0)

    ciphertext = aes.encrypt(payload)
log(ciphertext)



RE: Panasonic Viera TV IP control - admin - 14.04.2020

Here's Python code converted to Lua:
Code:
require('encdec')
aes = require('user.aes')

iv = encdec.base64dec('mUQdS7/RyJTMsiojPz9i1Q==')
iv_vals = { iv:byte(1, -1) }
key_vals = {}

for i = 1, 16, 4 do
  key_vals[ i ] = bit.band(bit.bnot(iv_vals[ i + 3 ]), 0xFF)
  key_vals[ i + 1 ] = bit.band(bit.bnot(iv_vals[ i + 2 ]), 0xFF)
  key_vals[ i + 2 ] = bit.band(bit.bnot(iv_vals[ i + 1 ]), 0xFF)
  key_vals[ i + 3 ] = bit.band(bit.bnot(iv_vals[ i ]), 0xFF)
end

key = string.char(unpack(key_vals))
hmac_key_mask = lmcore.hextostr('15C95AC2B08AA7EB4E228F811E34D04FA54BA7DCAC9879FA8ACDA3FC244F3854', true)
hmac_key_mask_vals = { hmac_key_mask:byte(1, -1) }
hmac_vals = {}

for i = 1, 32, 4 do
  hmac_vals[ i ] = bit.bxor(hmac_key_mask_vals[ i ], iv_vals[ bit.band(i + 1, 0xF) + 1 ])
  hmac_vals[ i + 1 ] = bit.bxor(hmac_key_mask_vals[ i + 1 ], iv_vals[ bit.band(i + 2, 0xF) + 1 ])
  hmac_vals[ i + 2 ] = bit.bxor(hmac_key_mask_vals[ i + 2 ], iv_vals[ bit.band(i - 1, 0xF) + 1 ])
  hmac_vals[ i + 3 ] = bit.bxor(hmac_key_mask_vals[ i + 3 ], iv_vals[ bit.band(i, 0xF) + 1 ])
end

hmac_key = string.char(unpack(hmac_vals))

authinfo = '<X_PinCode>4410</X_PinCode>'
payload = '000000000000'
n = #authinfo

payload = payload .. string.char(bit.band(bit.rshift(n, 24), 0xFF))
payload = payload .. string.char(bit.band(bit.rshift(n, 16), 0xFF))
payload = payload .. string.char(bit.band(bit.rshift(n, 8), 0xFF))
payload = payload .. string.char(bit.band(n, 0xFF))

payload = payload .. authinfo

aes_cbc, err = aes:new(key, nil, aes.cipher(128, 'cbc'), { iv = iv }, nil, 1)
ciphertext = aes_cbc:encrypt(payload)

sig = encdec.hmacsha256(ciphertext, hmac_key, true)
encrypted_payload = encdec.base64enc(ciphertext .. sig)



RE: Panasonic Viera TV IP control - benanderson_475 - 17.04.2020

(14.04.2020, 07:57)admin Wrote: Here's Python code converted to Lua:
 Thanks @admin
I have it working now  Smile  im not sure what is the best way to pass variables between functions is, i used storage... is there any way to make variables global and persistant in a user library also is there any improvements i can make to this script, still learning...

To get a pin to display on the tv call function request_pin_code()
once you have the pin displayed on the tv then call function authorise_pin_code('0080') with the pin from the tv, that completes the pairing process, the tv will show message "pairing process complete"
Then you can call send encrypted commands with function, send_key('NRC_POWER-ONOFF')  etc



Code:
encdec = require('encdec')
aes = require('user.aes')
require('json')

ip = '192.168.1.100'

name = 'My Remote'
URL_CONTROL_NRC = 'nrc/control_0'
URL_CONTROL_DMR = 'dmr/control_0'
URL_CONTROL_NRC_DEF = 'nrc/sdd_0.xml'

URN_RENDERING_CONTROL = 'schemas-upnp-org:service:RenderingControl:1'
URN_REMOTE_CONTROL = 'panasonic-com:service:p00NetworkControl:1'


function request(host, url, urn, action, args)
  local body, http, ltn12, sink, res, err

  ltn12 = require('ltn12')
  http = require('socket.http')
  body = [[<?xml version="1.0" encoding="utf-8"?>
  <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body><u:]] .. action .. [[ xmlns:u="urn:]] .. urn .. [[">]] .. args .. [[</u:]] .. action .. [[></s:Body>
  </s:Envelope>]]

  sink = {}

  res, err = http.request({
    url = 'http://' .. host .. ':55000/' .. url,
    method = 'POST',
    headers = {
      ['soapaction'] = '"urn:' .. urn .. '#' .. action .. '"',
      ['Content-Type'] = 'text/xml; charset=utf-8',
      ['Content-Length'] = #body,
    },
    sink = ltn12.sink.table(sink),
    source = ltn12.source.string(body),
  })

  if sink then
   -- log(table.concat(sink))
   
    return table.concat(sink)
  else
    return nil, err
  end
end


function encrypt_soap_payload(data, key, hmac_key, iv)
payload = '000000000000'
  n = #data

payload = payload .. string.char(bit.band(bit.rshift(n, 24), 0xFF))
payload = payload .. string.char(bit.band(bit.rshift(n, 16), 0xFF))
payload = payload .. string.char(bit.band(bit.rshift(n, 8), 0xFF))
payload = payload .. string.char(bit.band(n, 0xFF))

payload = payload .. data

aes_cbc, err = aes:new(key, nil, aes.cipher(128, 'cbc'), { iv = iv }, nil, 1)
  ciphertext = aes_cbc:encrypt(payload)
  sig = encdec.hmacsha256(ciphertext, hmac_key, true)
  encrypted_payload = encdec.base64enc(ciphertext .. sig)
  return encrypted_payload
end

function decrypt_soap_payload(data, key, hmac_key, iv)
  aes_cbc, err = aes:new(key, nil, aes.cipher(128, 'cbc'), { iv = iv }, nil, 0)
  decrypted = aes_cbc:decrypt(encdec.base64dec(data))
  decrypted = string.gsub(string.sub(lmcore.strtohex(decrypted), 33), '%x%x', function(value) return string.char(tonumber(value, 16)) end)
  return decrypted
end

function get_session_keys(enc_key)
  iv = encdec.base64dec(enc_key)
  iv_vals = { iv:byte(1, -1) }
  key_vals = {}

for i = 1, 16, 4 do
  key_vals[ i ] = iv_vals[ i + 2]
  key_vals[ i + 1 ] = iv_vals[ i + 3]
  key_vals[ i + 2 ] = iv_vals[ i ]
  key_vals[ i + 3 ] = iv_vals[ i+ 1]
  end
 
  sesh_key = string.char(unpack(key_vals))
  sesh_hmac_key = iv..iv
  sesh_iv = iv
 
  return sesh_key, sesh_hmac_key, sesh_iv
end

function request_session_id(app_id, sesh_key, sesh_hmac_key, sesh_iv)
  encinfo = encrypt_soap_payload('<X_ApplicationId>' .. app_id .. '</X_ApplicationId>',sesh_key, sesh_hmac_key, sesh_iv)
  params = ('<X_ApplicationId>'..app_id..'</X_ApplicationId>'.. '<X_EncInfo>'..encinfo..'</X_EncInfo>' )
  sesh_id = request(ip, URL_CONTROL_NRC, URN_REMOTE_CONTROL, 'X_GetEncryptSessionId', params)
  return sesh_id
end

function request_pin_code()
  pairing_res = request(ip, URL_CONTROL_NRC, URN_REMOTE_CONTROL, 'X_DisplayPinCode', '<X_DeviceName>' .. name .. '</X_DeviceName>')
  if pairing_res then
  challenge_Key = string.match(pairing_res,'<X_ChallengeKey>(.*)</X_ChallengeKey>')
   iv = encdec.base64dec(challenge_Key)
    storage.set('IV', iv)
  end
end


function authorise_pin_code(pincode)

  iv = storage.get('IV')
  iv_vals = { iv:byte(1, -1) }
  key_vals = {}

for i = 1, 16, 4 do
  key_vals[ i ] = bit.band(bit.bnot(iv_vals[ i + 3 ]), 0xFF)
  key_vals[ i + 1 ] = bit.band(bit.bnot(iv_vals[ i + 2 ]), 0xFF)
  key_vals[ i + 2 ] = bit.band(bit.bnot(iv_vals[ i + 1 ]), 0xFF)
  key_vals[ i + 3 ] = bit.band(bit.bnot(iv_vals[ i ]), 0xFF)
end

key = string.char(unpack(key_vals))
hmac_key_mask = lmcore.hextostr('15C95AC2B08AA7EB4E228F811E34D04FA54BA7DCAC9879FA8ACDA3FC244F3854', true)
hmac_key_mask_vals = { hmac_key_mask:byte(1, -1) }
hmac_vals = {}

for i = 1, 32, 4 do
  hmac_vals[ i ] = bit.bxor(hmac_key_mask_vals[ i ], iv_vals[ bit.band(i + 1, 0xF) + 1 ])
  hmac_vals[ i + 1 ] = bit.bxor(hmac_key_mask_vals[ i + 1 ], iv_vals[ bit.band(i + 2, 0xF) + 1 ])
  hmac_vals[ i + 2 ] = bit.bxor(hmac_key_mask_vals[ i + 2 ], iv_vals[ bit.band(i - 1, 0xF) + 1 ])
  hmac_vals[ i + 3 ] = bit.bxor(hmac_key_mask_vals[ i + 3 ], iv_vals[ bit.band(i, 0xF) + 1 ])
end

  hmac_key = string.char(unpack(hmac_vals))
   
  params = '<X_AuthInfo>' .. encrypt_soap_payload("<X_PinCode>" .. pincode .. "</X_PinCode>",  key, hmac_key, iv)  .. '</X_AuthInfo>'
  authorise_res = request(ip, URL_CONTROL_NRC, URN_REMOTE_CONTROL, 'X_RequestAuth', params)
  auth_res = string.match(authorise_res,'<X_AuthResult>(.*)</X_AuthResult>')
 
  decrypted = decrypt_soap_payload(auth_res, key, hmac_key, iv)
  app_id = string.match(decrypted,'<X_ApplicationId>(.*)</X_ApplicationId>')
  enc_key = string.match(decrypted,'<X_Keyword>(.*)</X_Keyword>')
 
  -- get AES and Hmac keys from x_keyword 
  sesh_key, sesh_hmac_key, sesh_iv = get_session_keys(enc_key)
  -- request session key to be able to send encrypted commands 
  sesh_id_res = request_session_id(app_id, sesh_key, sesh_hmac_key, sesh_iv)
  enc_result = string.match(sesh_id_res,'<X_EncResult>(.*)</X_EncResult>')
  enc_result = decrypt_soap_payload(enc_result, sesh_key, sesh_hmac_key, sesh_iv)
 
  --Set session ID and begin sequence number at 1. We have to increment the sequence number upon each successful NRC command.
  sesh_id = string.match(enc_result,'<X_SessionId>(.*)</X_SessionId>')
  sesh_seq_num = 1
 
  session_keys = json.encode({
       SESSION_ID = sesh_id,
       SESSION_SEQUENCE_NUMBER = sesh_seq_num,
       SESSION_IV = sesh_iv,
       SESSION_HMAC_KEY = sesh_hmac_key,
       SESSION_KEY = sesh_key,
       APP_ID = app_id,
       KEY = key,
       HMAC_KEY = hmac_key,
       IV = iv
        })
  storage.set('session_keys', session_keys)
  return sesh_id, sesh_seq_num, sesh_iv
 
end

function send_enc_cmd(action, urn, params)
   x = storage.get('session_keys')
  S_K =json.decode(x)
  -- modify sequence number to 8 bits
  seq_num = string.format("%08d",  S_K.SESSION_SEQUENCE_NUMBER)
 
  encrypted_command = [[<X_SessionId>]] .. S_K.SESSION_ID .. [[</X_SessionId>]]..
                      [[<X_SequenceNumber>]] .. seq_num ..[[</X_SequenceNumber>]]..
                      [[<X_OriginalCommand>]] .. [[<u:]].. action .. [[ xmlns:u="urn:]] .. urn .. [[">]] .. params .. [[</u:]] .. action .. [[>]] .. [[</X_OriginalCommand>]]
                                     
  enc_cmd = encrypt_soap_payload(encrypted_command, S_K.SESSION_KEY, S_K.SESSION_HMAC_KEY, S_K.SESSION_IV)
  params = '<X_ApplicationId>'..S_K.APP_ID..'</X_ApplicationId>'.. '<X_EncInfo>'.. enc_cmd..'</X_EncInfo>'
  send_enc_cmd_res = request(ip, URL_CONTROL_NRC, URN_REMOTE_CONTROL, 'X_EncryptedCommand', params)
  res_err = string.match(send_enc_cmd_res,'<s:Fault>')
 
  -- if send_enc_cmd_res is sucessfull then increment sequence number
  if not res_err then
    S_K.SESSION_SEQUENCE_NUMBER = S_K.SESSION_SEQUENCE_NUMBER + 1
   
  session_keys = json.encode({
       SESSION_ID = S_K.SESSION_ID,
       SESSION_SEQUENCE_NUMBER = S_K.SESSION_SEQUENCE_NUMBER, -- increment session sequence number each time command is sucessfull
       SESSION_IV = S_K.SESSION_IV,
       SESSION_HMAC_KEY = S_K.SESSION_HMAC_KEY,
       SESSION_KEY = S_K.SESSION_KEY,
       APP_ID = S_K.APP_ID,
       KEY = S_K.KEY,
       HMAC_KEY = S_K.HMAC_KEY,
       IV =S_K.IV
      })
    storage.set('session_keys', session_keys)
  end
 
  return params                 
end


function send_key(key)
  params = send_enc_cmd('X_SendKey', URN_REMOTE_CONTROL, '<X_KeyEvent>'..key..'</X_KeyEvent>')
end

function get_mute()
  req_res = request(ip, URL_CONTROL_DMR, URN_RENDERING_CONTROL, 'GetMute', '<InstanceID>0</InstanceID><Channel>Master</Channel>')
  mute_level = string.match(req_res,'<CurrentMute>(.*)</CurrentMute>')
  return mute_level
end

function get_volume()
  req_res = request(ip, URL_CONTROL_DMR, URN_RENDERING_CONTROL, 'GetVolume', '<InstanceID>0</InstanceID><Channel>Master</Channel>')
  vol_level = string.match(req_res,'<CurrentVolume>(.*)</CurrentVolume>')
  return vol_level
end

function set_mute(state)
  request(ip, URL_CONTROL_DMR, URN_RENDERING_CONTROL, 'SetMute', '<InstanceID>0</InstanceID><Channel>Master</Channel><DesiredMute>'.. state .. '</DesiredMute>')
end

function set_volume(level)
   request(ip, URL_CONTROL_DMR, URN_RENDERING_CONTROL, 'SetVolume', '<InstanceID>0</InstanceID><Channel>Master</Channel><DesiredVolume>'.. level .. '</DesiredVolume>')
end



RE: Panasonic Viera TV IP control - parkerc - 24.12.2020

Hi

id love to test this piece of code on a standard Lua installation, but to do that does anyone know where I could find the required modules/libraries ?

Code:
encdec = require('encdec')
aes = require('user.aes')

Is anyone able to post them or direct me to where I can download them ?


RE: Panasonic Viera TV IP control - admin - 25.12.2020

This code requires LuaJIT. It won't work with standard Lua because it does not have FFI library.


RE: Panasonic Viera TV IP control - parkerc - 27.12.2020

(25.12.2020, 13:49)admin Wrote: This code requires LuaJIT. It won't work with standard Lua because it does not have FFI library.

Can I assume an alternative Lua library (or libraries) could be used ? 

For example  to get around the encdec.base64dec() requirement, I used a base64 function/code from here - http://lua-users.org/wiki/BaseSixtyFour and added a specific new function called encdec_base64dec()

Code:
-- decoding
function encdec_base64dec(data)
    local b = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    data = string.gsub(data, '[^'..b..'=]', '')
    return (data:gsub('.', function(x)
        if (x == '=') then return '' end
        local r,f='',(b:find(x)-1)
        for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
        return r;
    end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
        if (#x ~= 8) then return '' end
        local c=0
        for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
        return string.char(c)
    end))
end

I would really appreciate any guidance on alternatives for other calls such as those below e.g hex to/from string and aes_cbc encrypt/decrypt etc.

Code:
lmcore.strtohex(decrypted)
lmcore.hextostr('15C95AC2B08AA7EB4E228F811E34D04FA54BA7DCAC9879FA8ACDA3FC244F3854', true)
aes_cbc:decrypt(encdec.base64dec(data))
aes_cbc:encrypt(payload)
encdec.hmacsha256(ciphertext, hmac_key, true)

Is something like https://github.com/somesocks/lua-lockbox or something similar potentially a viable alternative library?


RE: Panasonic Viera TV IP control - admin - 28.12.2020

For conversion from/to hex you can use this library: https://gist.github.com/yi/01e3ab762838d567e65d
lockbox seems to have all the required functionality (aes128 and hmac).