Jump to content
C4 Forums | Control4

Rexabyte

c4Forums Member
  • Posts

    236
  • Joined

  • Last visited

  • Days Won

    8

Posts posted by Rexabyte

  1. On 3/19/2024 at 3:55 PM, DanITman said:

    @Rexabyte is who you want to talk to.  I think LG changed something in their firmware that broke some IP control. 

    Recent models require connections to port 3001 (Secure Websocket) as they closed the original port that the driver used. There is a version of the driver available that supports most recent models that require port 3001.

    If your TV model mentions WebOS odds are the driver will work. 4.X versions of our driver will work on older models, 5.X for anything more recent.

  2. JSON_APP_DATA = [[{
      "serviceIds": {
        "UM_SAMSUNG2017": "gcn+",
        "UM_SAMSUNG2021": "gcn+",
        "UM_AMAZON": "com.playsportsnetwork.tourmalet",
        "UM_SONY_TV": "GCN Player - Live Player Streaming",
        "UM_NV_SHIELD": "com.playsportsnetwork.tourmalet"
      },
      "appName": "GCN+"
    }]]

    The "UM_AMAZON" ID is 'com.playsportsnetwork.tourmalet', not 'com.playsportsnetwork.tourmalet.tv'. I haven't checked the actual application ID but is that correct?

  3. Proxy ID is the ID of an actual proxy (television, security_panel, light_v2 etc.) attached to a driver, there can be several. If you wanted a variable in relation to a light for example you would need to target the Proxy ID.

    Device ID is the ID of encompassing device (driver). Some devices do not have a proxy, these should be flagged with a combo element in the XML portion of the driver meaning they are both a proxy & device driver and share an ID (Device ID). If I create a variable in code from a driver the resulting variable gets placed on the device (use Device ID to reference).

    In the composer tree it's typical that the first proxy device is merged into the encompassing device visually and it doesn't make its own branch. A good example are lights with keypads. The light_v2 proxy gets displayed on the device and the keypad gets branched to a new node.

    @South Africa C4 user
    Hope this helps!

  4. @mstafford388 Sorry you thought annex4 was gone. Been busy.

    I decided to move the entire infrastructure and rebuild it on AWS. Devices now talk directly with AWS IoT Core (MQTT). That solved a lot of maintenance and scale related stuff and the cost will end up being cheaper than spinning up new servers to support more controllers real-time. Leaves me more time to focus on making drivers, and not supporting the infrastructure around serving drivers & licenses.

    There's a new frontend facelift as well. I moved away from Meteor as it didn't scale well. Now using a static site created with Vue served over CloudFront. It talks to AWS AppSync for real-time pub/sub and its GraphQL API. Uses the latest Stripe checkout stuff as well, which is visually a major improvement over what we have now.

    Overall the new infrastructure is largely cloud services and usually 'serverless' which takes a lot of weight off me for maintenance related stuff. Images attached are just development stuff.

    Removed the required training going forward on the new site as well. I never liked it, always thought it was more of a hinderance to onboarding new users. The driver connecting to the platform is a 1 click install now as well as it gets an embedded certificate for licensing and authentication. You simply create the project on the platform and download the driver created for it, not much you can do wrong. Filenames no longer matter either.

    Checkout.png

    Drivers.png

  5. Not gone. Not sure who told you that. Bunch of changes coming September/October including some new drivers.

    All of our drivers at the moment are technically just served over Amazon S3 and we're not currently generating presigned URLs for download. Technically anyone with access can give you the link. I don't want to post them in a public forum but I'll PM them to you shortly.

  6. I did publish that extension and what I posted above will be an entirely new extension. It's such a major overhaul that I'll leave the old one as a lightweight completion extension for the functions and developers that have their own build methods. The new one is opinionated and requires a certain project structure for building the driver, but it does come with quite a few benefits.

    Here's some source code of an example driver: https://github.com/annex4-inc/generic-camera-driver

    You'll notice there is no driver.xml file, it's been split apart into the components folder and a smaller JSON object in the package.json file.

    EDIT: Should also mention the extension can import .c4z files to migrate to the expected project structure.

  7. I'm in the process of updating my VSCode extension I started long ago. I actually just added support for updating Lua script today after Ryan's comment.

    The formatting of the driver is quite different and there's no modifications inside of XML anymore. Most sections are broken out into JSON files for their specific purposes (properties, commands, events, connections, etc) and provide a webview for modification, you can still edit the underlying JSON file as well (backed by JSON schemas)

    I've also been able to include some improvements for things that bothered me, for example you can now specify the amount of HDMI connections you want to generate rather than copy pasting a bunch of XML blocks:
    image.png.ffcff5da695d0cd6901cd9fe639a2c02.png

    Properties & Webview example:
    image.png.44465d389ac462182198fcb0466752cc.png

    Auto completion of functions:
    image.png.1994113bd785869da99599be3d7e62d6.png

    Signature helpers:
    image.png.c47dda02fc4dc75d54e5b6c9981e8f1c.png

    All that being said I still haven't pushed a release as there are some issues to work out around the UI capability section.

    Repository here: https://github.com/annex4-inc/vscode-control4-ext

  8. 6 minutes ago, BuffaloC4 said:

    Did you ever get this working? 
    I bet people would employ you to do an Xbox driver for them. I know I would. 

    It was put on the back burner as we took on some non Control4 related projects.

    We never revisited it afterwards as the Series X released and it doesn't work over the same local communication method, it requires an internet connection and uses xCloud.

  9. I haven't touched the API in question but I can hopefully explain the time things can take and libraries.

    A while ago I wanted to create an Xbox driver that works over the SmartGlass protocol. While this would be relatively easy if you had the SDK that Microsoft provides it is much more difficult in a sandboxed environment (Control4) where you cannot use the SDK provided by Microsoft. Instead you need to work with the raw protocol (bits and bytes) and write all of the logic required just to even communicate to the Xbox. Sometimes you can find opensource MIT licensed code to do portions of what you need, other times you need to write it yourself.

    So what would it take to write everything required where the libraries don't exist in Control4? Let's start with JUST the crypto portion: https://openxbox.org/smartglass-documentation/cryptography/#key-exchange

    • On Discovery, the console responds with a Discovery Response including a certificate, this certificate holds the console's public key.
    • The client generates elliptic curve and derives the shared secret with console's public key using ECDH and a randomly generated public/private keypair
    • The shared secret is salted using static salts, see KDF Salts
    • The salted shared secret is hashed using SHA-512
    • The salted & hashed shared secret is split into the following individual keys:
    • bytes 0-16: Encryption key (AES-128-CBC)
    • bytes 16-32: Initialization Vector key (AES-128-CBC)
    • bytes 32-64: Hashing key (HMAC-SHA-256)
    • The client's public key is sent inside the Connect Request message to the console

    First things first we need to get the public key from the certificate. So we need to parse out the response and format it. It's received in an ASN. The following code (incomplete, needs to support more types, also using a custom library, BitHelper, to read bits and bytes) is used just to parse the ASN to get the public key used in step 2. After that we still need to do encryption using ECDH, which if not supported in Control4's encryption methods also needs to be written or sourced from somewhere. This can go on and on.
     

    
    ASN = (function()
        local class = {}; class.__index = class
    
        local Types = {
            BOOLEAN  = 0x01,
            INTEGER  = 0x02,
            STRING   = 0x03,
            OCTET    = 0x04,
            NULL     = 0x05,
            OBJECT   = 0x06,
            UTF8String = 0x0C,
            IA5STRING= 0x16,
            SEQUENCE = 0x30,
            SET      = 0x31,
        }
    
        class.Map = {
            [0x01] = "DecodeBoolean",
            [0x02] = "DecodeInteger",
            [0x03] = "DecodeString",
            [0x04] = "DecodeOctet",
            [0x05] = "DecodeNull",
            [0x06] = "DecodeObject",
            [0x0C] = "DecodeUTF8String",
            [0x13] = "DecodeUTF8String",
            [0x16] = "DecodeIA5String",
            [0x17] = "DecodeUTCTime",
            [0x30] = "DecodeSequence",
            [0x31] = "DecodeSet",
        }
    
        class.Class = {
            ["Universal"] = 0,
            ["Application"] = 1,
            ["Context"] = 2,
            ["Private" ] = 3
        }
    
        function class.DecodeSet(data, pos)
            Logger.Trace("ASN.DecodeSet: " .. pos)
            pos, l = class.DecodeLength(data, pos)
            pos, d = BitHelper.GetString(data, pos, l)
    
            local p, obj = class.Parse(d)
    
            return pos, obj
        end
    
        function class.DecodeOctet(data, pos)
            local l,d
    
            pos, l = class.DecodeLength(data, pos)
            pos, d = BitHelper.GetString(data, pos, l)
    
            return pos, d
        end
    
        function class.DecodeUTCTime(data, pos)
            Logger.Trace("ASN.DecodeUTCTime")
    
            pos, length = class.DecodeLength(data, pos)
    
            --print("STRING LENGTH: " .. length)
    
            pos, str = BitHelper.GetString(data, pos, length)
    
            --print("STRING: " .. str)
    
            return pos, str
        end
    
        function class.DecodeString(data, pos)
            Logger.Trace("ASN.DecodeString")
    
            pos, length = class.DecodeLength(data, pos)
    
            --print("STRING LENGTH: " .. length)
    
            pos, str = BitHelper.GetString(data, pos, length)
    
            --print("STRING: " .. str)
    
            return pos, str
        end
    
        function class.DecodeUTF8String(data, pos)
            Logger.Trace("ASN.DecodeUTF8String")
    
            pos, length = class.DecodeLength(data, pos)
    
            --print("STRING LENGTH: " .. length)
    
            pos, str = BitHelper.GetString(data, pos, length)
    
            --print("STRING: " .. str)
    
            return pos, str
        end
    
        function class.DecodeLength(data, pos)
            Logger.Trace("ASN.DecodeLength")
    
            local length = nil
    
            pos, length = BitHelper.GetInt8(data, pos)
    
            local isLong = bit.band(length, 0x80) ~= 0
    
            --print("LONG: " .. tostring(isLong))
    
            if (isLong) then
                local bytes = bit.band(length, 0x7F)
    
                Logger.Debug("BYTES: " .. bytes)
                Logger.Debug(hexdump(string.sub(data, pos, pos + bytes - 1)))
    
                if (bytes == 1) then
                    pos, length = BitHelper.GetInt8(data, pos)
                elseif (bytes == 2) then
                    pos, length = BitHelper.GetInt16(data, pos)
                elseif (bytes == 4) then
                    pos, length = BitHelper.GetInt32(data, pos)
                else
                    Logger.Error("BAD LENGTH")
                end
    
                --print("LENGTH: " .. length)
            else
                Logger.Debug("LENGTH: " .. length)
                Logger.Debug(hexdump(string.sub(data, pos - 1, pos)))
            end
    
            return pos, length
        end
    
        function class.DecodeSequence(data, pos)
            Logger.Trace("ASN.DecodeSequence: " .. pos)
            --print(hexdump(string.sub(data, pos)))
    
            pos, l = class.DecodeLength(data, pos)
            pos, d = BitHelper.GetString(data, pos, l)
    
            local p, obj = class.Parse(d)
    
            return pos, obj
        end
    
        function class.DecodeSequenceOf()
    
        end
    
        function class.DecodeInteger(data, pos)
            Logger.Trace("ASN.DecodeInteger")
    
            local start = pos
    
            pos, length = class.DecodeLength(data, pos)
    
            if (length == 1) then
                return BitHelper.GetInt8(data, pos)
            elseif (length == 2) then
                return BitHelper.GetInt16(data, pos)
            elseif (length == 4) then
                return BitHelper.GetInt32(data, pos)
            else
                return BitHelper.GetString(data, pos, length)
            end
        end
    
        function class.DecodeContext(data, pos)
            Logger.Trace("ASN.DecodeContext")
    
            local length, d
    
            pos, length = class.DecodeLength(data, pos)
            pos, d = BitHelper.GetString(data, pos, length)
    
            local _, obj = class.Parse(d)
    
            return pos, obj
        end
    
        function class.DecodeObject(data, pos)
            Logger.Trace("ASN.DecodeObject")
    
            local length = 0
    
            pos, length = class.DecodeLength(data, pos)
    
            local d = string.sub(data, pos, pos + length)
    
            local p, firstByte = BitHelper.GetInt8(d)
    
            local firstDigit = firstByte / 40;
            local secondDigit = firstByte % 40;
            local nodes = { firstDigit, secondDigit }
    
            -- TODO: Determine OID value
    
            return pos + length, d
        end
    
        function class.DecodeNull(data, pos)
            pos, length = class.DecodeLength(data, pos)
    
            return pos, false
        end
    
        function class.DecodeBoolean(data, pos)
            Logger.Trace("ASN.DecodeBoolean")
    
            pos, length = class.DecodeLength(data, pos)
            pos, d = BitHelper.GetInt8(data, pos)
    
            return pos, d
        end
    
        function class.DecodeChoice()
    
        end
    
        function class.Parse(certificate, pos)
            Logger.Trace("ASN.Parse")
    
            local pos = pos or 1
            local limit = 1
            local obj = {}
    
            while (pos < #certificate) do
                local t, l, d
    
                pos, t = BitHelper.GetInt8(certificate, pos)
    
                local f = bit.rshift(bit.band(t, 0x20), 5)
                local c = bit.rshift(bit.band(t, 0xC0), 6)
    
                local method = class.Map[t]
    
                if (method and class[method]) then
                    Logger.Trace(method)
    
                    pos, d = class[method](certificate, pos)
    
                    obj[#obj + 1] = d
                else
                    if (c == class.Class.Context) then
                        pos, d = class.DecodeContext(certificate, pos)
                    else
                        Logger.Error("BAD METHOD: " .. t)
                    end
                end
            end
            return pos, obj
        end
    
        local mt = {
            __call = function(self)
                local instance = {}
    
                setmetatable(instance, class)
    
                return instance
            end,
        }
    
        setmetatable(class, mt)
    
        return class
    end) ()



     

  10. If you set the 'Debug Mode' property to 'Debug' in the driver and replicate the issue I can take a look, just PM me the Lua output or open a ticket on our site and provide it there. Should tell me if its the driver causing it (by sending WOL packets) or something external (like CEC mentioned above.)

    The last time someone reported something similar they had setup programming to send a WOL command to power on the TV when they detected the TCP port disconnect (which happens on TV off.)

  11. Quote

    [11:26 AM PST] We are seeing impact to multiple AWS APIs in the US-EAST-1 Region. This issue is also affecting some of our monitoring and incident response tooling, which is delaying our ability to provide updates. Services impacted include: EC2, Connect, DynamoDB, Glue, Athena, Timestream, and Chime and other AWS Services in US-EAST-1. The root cause of this issue is an impairment of several network devices in the US-EAST-1 Region. We are pursuing multiple mitigation paths in parallel, and have seen some signs of recovery, but we do not have an ETA for full recovery at this time. Root logins for consoles in all AWS regions are affected by this issue, however customers can login to consoles other than US-EAST-1 by using an IAM role for authentication.

    https://status.aws.amazon.com/


  12. Keep in mind you can also just specify the certificate and use OpenSSL to encrypt the source directly. At which point you don't need driverpackager at all and can just create an archive.

    openssl smime -encrypt -binary -aes-256-cbc -in driver.lua -out driver.lua.encrypted -outform DER certificate.pem
    
    // certificate.pem containing the contents of the public key located here:
    // https://github.com/control4/drivers-driverpackager/blob/master/dp/encrypt_c4z.py
  13. On 6/5/2021 at 11:56 AM, South Africa C4 user said:

    I’ve been trying this on @Shivam Paw TTS driver but can’t seem to get it to work, so don’t think this works on the TTS drivers 😞

    Works on my Polly driver, just use the 'Synthesize SSML' command. Example Text:

    <speak>This is a <break time="3s"/> test.</speak>

    No need to escape or otherwise worry about the XML formatting.

  14. I believe it's a command ("GET_EXTRAS_SETUP") from the proxy to the protocol portion of the driver. You need to return the XML you specified above.
    You need to also handle the extras state ("GET_EXTRAS_STATE") to specify the values in each object.

    You can send the notifications to update the extras as well (5001 being the thermostat proxy ID):

    C4:SendToProxy(5001, "EXTRAS_SETUP_CHANGED", {XML = "XmlAsString"}, "NOTIFY")
    C4:SendToProxy(5001, "EXTRAS_STATE_CHANGED", {XML = "XmlAsString"}, "NOTIFY")

    Full example:

    local data = [[<extras_setup>
          <extra>
            <section label="Vane Swing">
              <object type="list" id="1" label="Vane direction">
                <list maxselections="1">
                  <item text="Auto" value="Auto"/>
                  <item text="Swing" value="Swing"/>
                  <item text="Ceiling" value="Ceiling"/>
                  <item text="High" value="High"/>
                  <item text="Middle" value="Middle"/>
                  <item text="Low" value="Low"/>
                  <item text="Floor" value="Floor"/>
                </list>
              </object>
            </section>
          </extra>
    </extras_setup>]]
    
    C4:SendToProxy(5001, "EXTRAS_SETUP_CHANGED", {XML = data}, "NOTIFY")

     

  15. There should be a terminal property in the primary driver. Take one of the shades being listed in the properties (example below is 'DEF') that isn't communicating and enter the following in the terminal:

    !DEFr?;

    If it comes back with !DEFEpl; the shade is unable to report or notify of intermediate positions which means the driver can't sync realtime. If you're getting !DEFr###b### it could be an issue with the driver in which case PM me and I'll take a further look.

    One other question, are you using a DC power panel?

  16. The only difference between the Gen.1 and Gen.2 options are the port, so if it's communicating over port 80 you can leave it with that. The Shade ID's likely don't match up to the previous ones if they were all replaced. The binding logic works based on the shade ID that comes back from the JSON object, if the IDs are all different then it won't be able to find the appropriate binding, hence the following lines:

    PowerviewAdapter.pollPositions: Unable to get binding for shade:

    I would clear the shades (Clear Shades action in the driver) and retrieve them again, it will clear the mapping to the old ID's and create new ones.

    If you're still having issues after the above suggestion, PM me and I'll take a further look. 

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.