Is it possible to commercialize a SmartThings Direct Connect device without WWST certification?

Hello,

I am currently integrating the SmartThings Direct Connect SDK into an ESP32-based ventilation product.

Initially, I planned to obtain WWST certification before launching the product. However, as I added more features, I found that standard Capabilities alone were not sufficient to represent the full functionality of our device, which led me to use Custom Capabilities.

That said, I came across the following note in the official documentation:

“Devices with custom Capabilities are not supported by the Works with SmartThings (WWST) certification program at this time.”

This has created a roadblock for us. To control our device’s advanced features through the SmartThings app, Custom Capabilities are necessary — but using them appears to block WWST certification entirely.

I have a few questions related to this situation:

1. Is it possible to commercialize a SmartThings Direct Connect device without WWST certification?

The official documentation states “you are still able to connect your device with SmartThings” even without certification, which suggests basic connectivity remains possible. However, it is unclear whether this extends to selling the product commercially.

2. What are the actual limitations of not having WWST certification?

My understanding is that the main restrictions are: no WWST badge, no listing in the official SmartThings app device catalog, and no Samsung.com product exposure — but that users can still connect manually via QR code. Is this correct?

3. Is there currently no path to WWST certification when using Custom Capabilities?

The phrase “at this time” in the documentation gives the impression that support may be added in the future. Is there any known roadmap or expected timeline for this? Additionally, would a “certification by similarity” approach work here — for example, certifying with only standard Capabilities first, then adding Custom Capabilities later?

4. What steps are required to commercialize a device in a non-certified state?

With device development complete, if we were to proceed with a commercial launch without WWST certification, what process or precautions would apply? Any guidance or pointers to official documentation would be greatly appreciated.


Also — is partners@smartthings.com the right contact for this type of commercialization inquiry, or is there a more appropriate channel?

Thank you in advance.

Tagging @nayelyz .

Hi, @JohnJo

No, this integration requires you to upload a data file to SmartThings with the serial numbers and other info so that they can onboard correctly for other users. Currently, it works for you because you’re using the same account that the integration was created with.

This is not the case for Direct-Connected devices since the information I mentioned above is required.

We don’t have information about this changing in the future or any plans/roadmaps.
What we can do now is:
Please list and describe the features your device has that are not included in the standard capabilities, so I can ask the engineering team about them.

As I mentioned before, this is not the case for Direct-Connected devices.

It is certainly an option. Once you provide the list of features I mentioned before. I’ll let the Partners team know about this case to see what we can do, but you can also send them an email directly, including such a list so they have a better context.

That’s surprising… So to confirm, Custom Capabilities are supported by the platform, but using them means we cannot commercially release a product? I’m not sure what the point of Custom Capabilities is then.

Anyway, to summarize: using Custom Capabilities means we cannot obtain WWST certification, and without WWST certification, we cannot commercially launch a product with SmartThings integration — is that correct?

And here is a list of the Custom Capabilities I have created:

The highlighted area in yellow in the screenshot above represents the Custom Capability I created.
The device has three Operation Modes: CO2, Dust Detection, and Temperature. Each mode automatically adjusts the fan speed based on the sensor reading, and the Custom Capability allows the user to configure the threshold range for that adjustment.
For example, when the sensor value reaches the Min Threshold, the fan speed is set to 1 (minimum). When it reaches the Max Threshold, the fan speed is set to 100 (maximum). The values in between are linearly interpolated.
The Auto ON/OFF feature is an additional option within each mode. When enabled, the device turns off completely (fan speed = 0) when the sensor value drops to the Min Threshold, and turns back on at full speed when it rises back to the Max Threshold.
Below are the capability and presentation JSON files for the CO2 mode. The Dust and Temperature modes share the same structure with only the attribute names and units changed.

co2Settings.json

{
  "name": "CO2 Settings",
  "attributes": {
    "carbonMin": {
      "schema": {
        "type": "object",
        "properties": {
          "value": { "type": "integer", "minimum": 0, "maximum": 900 },
          "unit": { "type": "string", "enum": ["ppm"], "default": "ppm" }
        },
        "additionalProperties": false,
        "required": ["value"]
      },
      "setter": "setCarbonMin",
      "enumCommands": []
    },
    "carbonMax": {
      "schema": {
        "type": "object",
        "properties": {
          "value": { "type": "integer", "minimum": 1000, "maximum": 2000 },
          "unit": { "type": "string", "enum": ["ppm"], "default": "ppm" }
        },
        "additionalProperties": false,
        "required": ["value"]
      },
      "setter": "setCarbonMax",
      "enumCommands": []
    },
    "carbonAction": {
      "schema": {
        "type": "object",
        "properties": {
          "value": { "type": "string", "enum": ["on", "off"] }
        },
        "additionalProperties": false,
        "required": ["value"]
      },
      "enumCommands": []
    }
  },
  "commands": {
    "setCarbonMin": {
      "name": "setCarbonMin",
      "arguments": [
        {
          "name": "min",
          "optional": false,
          "schema": { "type": "integer", "minimum": 0, "maximum": 900 }
        }
      ]
    },
    "setCarbonMax": {
      "name": "setCarbonMax",
      "arguments": [
        {
          "name": "max",
          "optional": false,
          "schema": { "type": "integer", "minimum": 1000, "maximum": 2000 }
        }
      ]
    },
    "on": {
      "name": "on",
      "arguments": []
    },
    "off": {
      "name": "off",
      "arguments": []
    }
  }
}

co2Settings_presentation.json

{
  "dashboard": {
    "states": [],
    "actions": []
  },
  "detailView": [
    {
      "label": "CO2 최솟값",
      "displayType": "slider",
      "slider": {
        "command": "setCarbonMin",
        "value": "carbonMin.value",
        "unit": "carbonMin.unit",
        "range": [0, 900],
        "step": 100
      }
    },
    {
      "label": "CO2 최댓값",
      "displayType": "slider",
      "slider": {
        "command": "setCarbonMax",
        "value": "carbonMax.value",
        "unit": "carbonMax.unit",
        "range": [1000, 2000],
        "step": 100
      }
    },
    {
      "label": "자동 ON/OFF",
      "displayType": "standbyPowerSwitch",
      "standbyPowerSwitch": {
        "command": {
          "on": "on",
          "off": "off"
        },
        "state": {
          "value": "carbonAction.value",
          "on": "on",
          "off": "off"
        }
      }
    }
  ],
  "automation": {
    "conditions": [],
    "actions": []
  }
}

Devie profile

{
  "deviceProfile": {
    "id": "531bd3fb-e6fa-45ff-9e33-9b073a0385c1",
    "name": "v2.1.2",
    "metadata": {
      "deviceType": "Vent",
      "ocfDeviceType": "x.com.st.d.vent",
      "deviceTypeId": "Vent",
      "manufacturerName": "snug",
      "mnId": "Uimh",
      "vid": "d7f4abc9-bb6f-4eb6-b158-4f423b97fc6a",
      "mnmn": "snug",
      "presentationId": "snug.d7f4abc9-bb6f-4eb6-b158-4f423b97fc6a",
      "integrationType": "direct",
      "mainState": "main~switch~1",
      "ocfSpecVer": "core 1.1.0",
      "mainAction": "main~switch~1",
      "resourceType": "x.com.st.d.vent"
    },
    "migrationStatus": "NOT_MIGRATED",
    "status": "DEVELOPMENT",
    "preferences": [],
    "components": [
      {
        "label": "  ",
        "id": "main",
        "capabilities": [
          {
            "id": "healthCheck",
            "version": 1,
            "optional": false,
            "ephemeral": false
          },
          {
            "id": "switch",
            "version": 1,
            "optional": false,
            "ephemeral": false
          },
          {
            "id": "fanSpeed",
            "version": 1,
            "optional": false,
            "ephemeral": false
          },
          {
            "id": "mode",
            "version": 1,
            "optional": false,
            "ephemeral": false
          },
          {
            "id": "fanMode",
            "version": 1,
            "optional": false,
            "ephemeral": false
          },
          {
            "id": "snug.co2Settings",
            "version": 1,
            "optional": false,
            "ephemeral": false
          },
          {
            "id": "snug.dustSettings",
            "version": 1,
            "optional": false,
            "ephemeral": false
          },
          {
            "id": "snug.temperatureSettings",
            "version": 1,
            "optional": false,
            "ephemeral": false
          }
        ],
        "categories": [
          {
            "name": "Vent",
            "categoryType": "manufacturer"
          }
        ],
        "optional": false
      },
      {
        "label": " ",
        "id": "sensor",
        "capabilities": [
          {
            "id": "temperatureMeasurement",
            "version": 1,
            "optional": false,
            "ephemeral": false
          },
          {
            "id": "dustSensor",
            "version": 1,
            "optional": false,
            "ephemeral": false
          },
          {
            "id": "carbonDioxideMeasurement",
            "version": 1,
            "optional": false,
            "ephemeral": false
          }
        ],
        "categories": [],
        "optional": false
      }
    ]
  },
  "deviceConfiguration": {
    "mnmn": "snug",
    "vid": "d7f4abc9-bb6f-4eb6-b158-4f423b97fc6a",
    "version": "0.0.1",
    "type": "profile",
    "dashboard": {
      "states": [
        {
          "component": "main",
          "capability": "switch",
          "version": 1,
          "idx": 0,
          "group": "main",
          "values": [],
          "composite": false
        }
      ],
      "actions": [
        {
          "component": "main",
          "capability": "switch",
          "version": 1,
          "idx": 0,
          "group": "main"
        }
      ],
      "basicPlus": []
    },
    "detailView": [
      {
        "component": "main",
        "capability": "healthCheck",
        "version": 1,
        "values": [],
        "patch": []
      },
      {
        "component": "main",
        "capability": "switch",
        "version": 1,
        "values": [],
        "patch": []
      },
      {
        "component": "main",
        "capability": "fanSpeed",
        "version": 1,
        "values": [
          {
            "enabledValues": [],
            "range": [
              0,
              100
            ],
            "step": 25,
            "key": "fanSpeed.value"
          }
        ],
        "patch": [
          {
            "op": "replace",
            "path": "/0/slider/alternatives",
            "value": [
              {
                "key": "0",
                "value": "1",
                "type": "active"
              },
              {
                "key": "1",
                "value": "1",
                "type": "active"
              },
              {
                "key": "2",
                "value": "2",
                "type": "active"
              },
              {
                "key": "3",
                "value": "3",
                "type": "active"
              },
              {
                "key": "4",
                "value": "4",
                "type": "active"
              }
            ]
          }
        ],
        "visibleCondition": {
          "value": "fanMode.value",
          "operator": "ONE_OF",
          "operand": "[\"auto\",\"turbo\"]",
          "component": "main",
          "capability": "fanMode",
          "version": 1,
          "hideOnUnmatch": false
        }
      },
      {
        "component": "main",
        "capability": "fanMode",
        "version": 1,
        "values": [],
        "patch": [
          {
            "op": "replace",
            "path": "/0/label",
            "value": "동작모드"
          },
          {
            "op": "replace",
            "path": "/0/list/command/alternatives",
            "value": [
              {
                "key": "auto",
                "value": "수동",
                "type": "active"
              },
              {
                "key": "high",
                "value": "온도",
                "type": "active"
              },
              {
                "key": "medium",
                "value": "CO2감지",
                "type": "active"
              },
              {
                "key": "low",
                "value": "연기감지",
                "type": "active"
              },
              {
                "key": "turbo",
                "value": "주기환기",
                "type": "active"
              }
            ]
          },
          {
            "op": "replace",
            "path": "/0/list/state/alternatives",
            "value": [
              {
                "key": "auto",
                "value": "수동",
                "type": "active"
              },
              {
                "key": "high",
                "value": "온도",
                "type": "active"
              },
              {
                "key": "medium",
                "value": "CO2감지",
                "type": "active"
              },
              {
                "key": "low",
                "value": "연기감지",
                "type": "active"
              },
              {
                "key": "turbo",
                "value": "주기환기",
                "type": "active"
              },
              {
                "key": "off",
                "value": "꺼짐",
                "type": "inactive"
              }
            ]
          }
        ],
        "visibleCondition": {
          "value": "fanMode.value",
          "operator": "ONE_OF",
          "operand": "[\"auto\",\"high\",\"medium\",\"low\",\"turbo\"]",
          "component": "main",
          "capability": "fanMode",
          "version": 1,
          "hideOnUnmatch": false
        }
      },
      {
        "component": "main",
        "capability": "mode",
        "version": 1,
        "values": [
          {
            "enabledValues": [],
            "label": "주기",
            "key": "mode.value"
          }
        ],
        "patch": [],
        "visibleCondition": {
          "value": "fanMode.value",
          "operator": "EQUALS",
          "operand": "turbo",
          "component": "main",
          "capability": "fanMode",
          "version": 1,
          "hideOnUnmatch": true
        }
      },
      {
        "component": "main",
        "capability": "snug.co2Settings",
        "version": 1,
        "values": [],
        "patch": [],
        "visibleCondition": {
          "value": "fanMode.value",
          "operator": "EQUALS",
          "operand": "medium",
          "component": "main",
          "capability": "fanMode",
          "version": 1,
          "hideOnUnmatch": true
        }
      },
      {
        "component": "main",
        "capability": "snug.dustSettings",
        "version": 1,
        "values": [],
        "patch": [],
        "visibleCondition": {
          "value": "fanMode.value",
          "operator": "EQUALS",
          "operand": "low",
          "component": "main",
          "capability": "fanMode",
          "version": 1,
          "hideOnUnmatch": true
        }
      },
      {
        "component": "main",
        "capability": "snug.temperatureSettings",
        "version": 1,
        "values": [],
        "patch": [],
        "visibleCondition": {
          "value": "fanMode.value",
          "operator": "EQUALS",
          "operand": "high",
          "component": "main",
          "capability": "fanMode",
          "version": 1,
          "hideOnUnmatch": true
        }
      },
      {
        "component": "sensor",
        "capability": "temperatureMeasurement",
        "version": 1,
        "values": [],
        "patch": []
      },
      {
        "component": "sensor",
        "capability": "dustSensor",
        "version": 1,
        "values": [],
        "patch": []
      },
      {
        "component": "sensor",
        "capability": "carbonDioxideMeasurement",
        "version": 1,
        "values": [],
        "patch": []
      }
    ],
    "automation": {
      "conditions": [
        {
          "component": "main",
          "capability": "healthCheck",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "main",
          "capability": "switch",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "main",
          "capability": "fanSpeed",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "main",
          "capability": "mode",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "main",
          "capability": "fanMode",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "main",
          "capability": "snug.co2Settings",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "main",
          "capability": "snug.dustSettings",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "main",
          "capability": "snug.temperatureSettings",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "sensor",
          "capability": "temperatureMeasurement",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "sensor",
          "capability": "dustSensor",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "sensor",
          "capability": "carbonDioxideMeasurement",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        }
      ],
      "actions": [
        {
          "component": "main",
          "capability": "healthCheck",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "main",
          "capability": "switch",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "main",
          "capability": "fanSpeed",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "main",
          "capability": "mode",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "main",
          "capability": "fanMode",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "main",
          "capability": "snug.co2Settings",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "main",
          "capability": "snug.dustSettings",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        },
        {
          "component": "main",
          "capability": "snug.temperatureSettings",
          "version": 1,
          "values": [],
          "patch": [],
          "exclusion": []
        }
      ]
    },
    "presentationId": "d7f4abc9-bb6f-4eb6-b158-4f423b97fc6a",
    "manufacturerName": "snug"
  }
}

I have one more question related to this topic.

When I look at Samsung’s own devices connected to my SmartThings account — such as my TV and air purifier — they appear to have a wide range of features and controls in the app that go well beyond what standard Capabilities seem to cover (as shown in the screenshots attached).

Is this achieved through a mechanism that is not available to third-party developers, such as an internal or proprietary capability system exclusive to Samsung products? Or is there a path for partner/third-party devices to achieve a similar level of UI richness within the SmartThings app?

Without wishing to derail this thread at all, as it has raised some interesting issues, might I ask what has led you down the path of custom capabilities? On the face of it your custom capabilities seem to be covering pretty much the same ground as device preferences.

I remember I requested to have Preferences in direct-connected integrations and that ticket is closed.
I see this sample mentions “preferences,” but I don’t know if it refers to the same ones we add to a device profile:

I’ll ask the Direct-Connected devices team about this.

Hi, @JohnJo
I shared your case with the Partners team and also asked about the device preferences since we’ve seen them mainly for Hub-Connected devices to see if it’s an option.
I’ll let you know their feedback.

Generally, they use a special UI, and, unfortunately, I haven’t seen this available for third-party integrations.

@orangebucket @nayelyz Thank you both for the input! :slight_smile:

I want to make sure I’m understanding correctly — just to confirm:

@orangebucket, you’re pointing out that what I’m trying to do with Custom Capabilities might already be achievable with Device Preferences — and asking why I went with Custom Capabilities instead, right?

And @nayelyz, are you saying that Device Preferences isn’t currently available for Direct-Connected devices? It sounds like there was a ticket requesting that Preferences be added to Direct-Connected integrations, but it was closed without being implemented — is that correct?

By the way, could you help me understand what Device Preferences actually is? From the discussion it sounds like it’s separate from Capabilities — is it something like a settings UI in the app where users can adjust device parameters? I wasn’t familiar with it before this thread.

Either way, glad to have learned something new here! :slight_smile:

I’m still investigating this; I cannot say for sure now.

When you add the property “preferences” with some options to the device profile. A new option called “Settings” appears in the device’s menu.
They allow us to receive user input, but we cannot modify it (in the user’s name) through code.

If you add them to your profile, they will appear because they’re recognized in the UI. The issue would be “catching” the value the user selected in our integration. I don’t know if Direct-Connected integration has an event for this. That’s what I’m trying to figure out.

Hi, @JohnJo
Following up on this. The engineering team mentioned there’s a way to work with device preferences in Direct-Connected projects but you would need to follow some special steps:

Here are further instructions. This document mentions the Developer Workspace but you should use the Developer Console:

Basically, you need to:
1.Create the Device Profile in the console
2. Onboarding the test device as usual
3. Find the device instance and get the profile ID using the SmartThings CLI.

--list devices
smartthings devices 

--get the device's details
smartthings devices deviceID -j
  1. You’ll find the deviceProfileId property there, which you’ll use for the next step
  2. Update the actual Device Profile to include preferences using the smartthings-cli.

I’m still discussing some details with the engineering team, but you can test this part and see if it helps your case.