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.
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
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.
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.
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.
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
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).
+1 for this feature request.
Hopefully, it will be approved someday.