Query profile name from a device

Is there a way to figure out which profile was used to create a device? There doesn’t appear to be any field I can look at that gives me the original profile name used during device creation. I would like to use it to choose the correct subdriver. If there isn’t a way to get it, I plan to create dummy capabilities used simply for tagging the devices.

Hi @blueyetisoftware

I couldn’t figure it out either and I saw in a stock driver that to retrieve profiles name they created a table with the fingerprint and the name of the profile, but I can’t remember the driver.

What I do is put a preference in each profile with a specific name for each profile or with a unique value for each profile.

I like the preference because it is permanent and recovers easily.

When I create a virtual LAN devices, I sometimes save the profile name in the “vendor_provided_label” metadata field which is retrieved with device.vendor_provided_label.

I also think that in these LAN devices it can be recovered with device.profile, but I’m not sure

1 Like

Hi, @blueyetisoftware!
Is this to assign a certain subdriver to a device discovered by a LAN driver?

Yes. I have a few different devices that use profiles with the same capabilities, but different vids and internal logic, so I can’t choose a subdriver based on capability. I also can’t make use of the model for my use case.

I tried that, but it is empty. profile doesn’t contain anything in my LAN drivers.

HI @blueyetisoftware

What device.profile has on my virtual LAN devices is a table with the entire profile, minus the name.

2022-10-11T11:20:13.228040745+00:00 PRINT Zigbee Temp Sensor and Child Thermostat Mc <<<< device.profile >>>>> table: 0x286dd70

Printing that table shows all the components and their capabilities.

print("<<<< device.profile >>>>>",utils.stringify_table(device.profile))

2022-10-11T11:20:13.281830745+00:00 PRINT Zigbee Temp Sensor and Child Thermostat Mc <<<< device.profile >>>>> {components={main={capabilities={legendabsolute60149.fanCyclicMode={id=“legendabsolute60149.fanCyclicMode”, version=1}, legendabsolute60149.fanNextChange={id=“legendabsolute60149.fanNextChange”, version=1}, legendabsolute60149.infoPanel={id=“legendabsolute60149.infoPanel”, version=1}, legendabsolute60149.thermostatLocked={id=“legendabsolute60149.thermostatLocked”, version=1}, relativeHumidityMeasurement={id=“relativeHumidityMeasurement”, version=1}, temperatureMeasurement={id=“temperatureMeasurement”, version=1}, thermostatCoolingSetpoint={id=“thermostatCoolingSetpoint”, version=1}, thermostatFanMode={id=“thermostatFanMode”, version=1}, thermostatHeatingSetpoint={id=“thermostatHeatingSetpoint”, version=1}, thermostatMode={id=“thermostatMode”, version=1}, thermostatOperatingState={id=“thermostatOperatingState”, version=1}}, emit_event=function: 0x28ad070, id=“main”}}, id=“f8e97036-2dcc-345f-8957-903280d6ce63”}

If you read that table you can find all the capabilities and find the one you need to identify the correct profile

 for id, value in pairs(device.profile.components.main.capabilities) do
    print("<<<< device.profile.components.main.capabilities[id]",id)
 end

device.profile.components.main.capabilities[id]      legendabsolute60149.thermostatLocked
device.profile.components.main.capabilities[id]      thermostatMode
device.profile.components.main.capabilities[id]      relativeHumidityMeasurement
device.profile.components.main.capabilities[id]      temperatureMeasurement
device.profile.components.main.capabilities[id]      legendabsolute60149.fanNextChange
device.profile.components.main.capabilities[id]      legendabsolute60149.fanCyclicMode
device.profile.components.main.capabilities[id]      thermostatHeatingSetpoint
device.profile.components.main.capabilities[id]      thermostatOperatingState
device.profile.components.main.capabilities[id]      legendabsolute60149.infoPanel
device.profile.components.main.capabilities[id]      thermostatCoolingSetpoint 
device.profile.components.main.capabilities[id]      thermostatFanMode

I don’t know if this can help you

Interesting. I was using json.encode and it produces an empty object.

print("<device.profile>",utils.stringify_table(device.profile))
print("(device.profile)",json.encode(device.profile))

In either case, I can’t choose the subdriver based on capability. The only difference is in the presentation. The different subdrivers populate different UIs. I’ll just make a tagging capability. It’s fairly easy and I know the ST team used to use that trick in older DTHs.

Hi, @blueyetisoftware
Indeed, the team mentioned the metadata info isn’t included in the device.profile object.

So, the best approach is the one suggested by @Mariano_Colmenarejo of using the fields model,vendor_provided_label or manufacturer that are used in driver:try_create_device

  log.info("Model: " .. device.model)
  log.info("Vendor provided label: " .. device.vendor_provided_label)
  log.info("Mfr: " .. device.manufacturer)

You’re using the can_handle function to define which subdriver will be used but you’re checking which comparison is best, right?
Have you tested that suggestion?

Yes, this is in the can_handle, but I don’t want to change the device info provided by the manufacturer. This is for a hub-to-hub integration, so my device info primarily comes from the Hub’s API and what it reports for capabilities of connected devices during discovery. Capabilities aren’t chosen based on these device fields, but instead based on the API results.

My ideal scenario would be the ability to dynamically create a profile. In the interim, I’ve had to create a bunch of variations of the same profile that I choose depending on the capabilities reported by the devices.

I could change the fields you mention, but that changes the meaning of those fields which I don’t love. Is there any reason a tagging capability with no attribute/commands wouldn’t be desirable? I know ST used to use that to identify bridge objects:

id: bridge
version: 1
status: deprecated
name: Bridge
ephemeral: false
attributes: {}
commands: {}

I found a cleaner solution using device:set_field. I am storing information obtained from the API in device fields. Then I can use that to pick the driver. Nice thing is that is changes with the API as well rather than be static. Thanks for being a sounding board.

2 Likes

The thing that became clear for me is that can_handle is called for every event, not just on init. So I can just store the info in a device field rather than a profile.

2 Likes

I had this problem too

I have a generic custom log capability I use on all my drivers, to aid in debug and to allow users to see what’s going on without logcat. This capability is invisible in all views and can only be seen in the history tab.

I then give that log a meaningful Id in the profile of each device.

- id: activitylogger
  label: activitylogger
  capabilities:
  - id: universevoice35900.log
    version: 1

This then allows me then do the following in code to figure out my device type, as each device type has an instance of universevoice35900.log, with a different Id, of course you could do this with any capability.

can_handle = function(opts, driver, device, ...)
    return device:component_exists("activitylogger")
  end,

I hope this helps

4 Likes

This is bizarre. Will they ever fix it so we can get profile more easily?

Hi!
I’ll open a feature request. Just a heads-up, it can take some time to be reviewed and implemented (if it’s approved).

2 Likes