How can I update the status of a virtual contact sensor to CLOSE state using curl


After the Groovy deprecation a lot of my automation stopped working.

How can I update the status of a virtual contact sensor to CLOSE state using curl after my hub was moved to edge drivers ?

I created a new device from Samsung account

I checked the API documentation, but it doesn’t work when I make the request.

Currently the sensor is in OPEN state.

curl -H “Authorization: Bearer token” -H “Content-Type: application/json” -X POST
nds -d ‘{“commands”: [{“component”: “main”,“capability”: “contactSensor”,“command”: “setOpen”,“arguments”: [“close”]}]}’
{“requestId”:“712516891090281429”,“error”:{“code”:“ConstraintViolationError”,“message”:“The request is malformed.”,“details”:[{“code”:“NotValidValue”,“target”:“commands[0].Command(component=main, capability=contactSensor, command=setOpen, commandId=null, arguments=[close])”,“message”:“setOpen is not a valid value.”,“details”:}]}}

The GET request is working fine:

curl -i -v -H 'Authorization: Bearer real_token' -X GET "" -k
Note: Unnecessary use of -X or --request, GET is already inferred.
* Trying [](
* Connected to []( ([]( port 443 ([#0](
* ALPN: offers http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN: server accepted http/1.1
* Server certificate:
* subject: CN=*.[](
* start date: Feb 22 00:00:00 2023 GMT
* expire date: Mar 22 23:59:59 2024 GMT
* issuer: C=US; O=Amazon; CN=Amazon RSA 2048 M01
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
> GET /v1/devices/df184b34-db74-4ee6-b86f-c4435e6ab670/status HTTP/1.1
> Host: [](
> User-Agent: curl/7.84.0
> Accept: */*
> Authorization: Bearer real_token
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Date: Thu, 10 Aug 2023 14:35:01 GMT
Date: Thu, 10 Aug 2023 14:35:01 GMT
< Content-Type: application/json
Content-Type: application/json
< Content-Length: 122
Content-Length: 122
< Connection: keep-alive
Connection: keep-alive
< Server: openresty
Server: openresty
< X-RateLimit-Limit: 350
X-RateLimit-Limit: 350
< X-RateLimit-Remaining: 350
X-RateLimit-Remaining: 350
< X-RateLimit-Reset: 59185
X-RateLimit-Reset: 59185
< X-RateLimit-Limit: 350
X-RateLimit-Limit: 350
< X-RateLimit-Remaining: 350
X-RateLimit-Remaining: 350
< X-RateLimit-Reset: 59078
X-RateLimit-Reset: 59078
< Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
< Access-Control-Allow-Headers: DNT,Keep-Alive,User-Agent,If-Modified-Since,Cache-Control,Content-Type,Accept,Authorization,X-ST-Client,X-ST-Api-Version,X-ST-Client-AppVersion,X-ST-Client-OS,X-ST-Client-DeviceModel
Access-Control-Allow-Headers: DNT,Keep-Alive,User-Agent,If-Modified-Since,Cache-Control,Content-Type,Accept,Authorization,X-ST-Client,X-ST-Api-Version,X-ST-Client-AppVersion,X-ST-Client-OS,X-ST-Client-DeviceModel

* Connection [#0]( to host []( left intact


Hi, @Ionut_Marian. Welcome to the SmartThings Community!

It is expected this won’t work since the capability contactSensor doesn’t have any commands, so, the value you used of “setOpen” isn’t valid.
You can verify this by getting the capability’s definition using the CLI command of smartthings capabilities contactSensor -j or this API call{capabilityId}/{capabilityVersion}

Now, to change the status of the contact sensor, you must send a “device event” for the virtual device which can be done with this CLI command:

smartthings virtualdevices:events deviceId contactSensor:contact closed

Which makes the request to, the body looks like this:


Please, let me know if you have any questions.



That makes sense now, thank you!

Meanwhile I used a virtual switch, I didn’t know what to do and that one worked with:

    "commands": [
        "component": "main",
        "capability": "switch",
        "command": "off",
        "arguments": []

It all really comes down to an appreciation of the capability definition which you can pull from the API as Nayely showed.

A capability like ‘switch’ defines ‘enum’ commands that are used to set particular attribute values. So it is known, for example, that using the ‘on’ command should result in the ‘switch’ attribute being set to ‘on’. So that has been made part of the default behaviour of a virtual device as you have found.

Similarly there are capabilities that define ‘setter’ commands for the attributes so it is known that a particular command and argument should set a particular attribute to the argument value. So that is the default behaviour too.

There may be a number of other commands supported by a capability that aren’t defined for the generic cases like the above but that it might be possible to define for specific use cases. Custom virtual devices allow command mappings to be made to define what should happen to particular attributes. I don’t know if any of the standard virtual devices also use this.

Then there are capabilities that simply don’t have commands such as contactSensor. With those the attribute values have to be set directly using device events as again you have found.

1 Like