GithubHelp home page GithubHelp logo

tradfri-go's Introduction

tradfri-go

CircleCI Go Report Card

Native Go implementation for talking CoAP to a IKEA Trådfri gateway over DTLS 1.2.

Note: The author is not in any way affiliated or related to IKEA, this is purely a hobby project!

Note 2: The application is not really being actively developed and may and have bugs and known issues! Will happily accept PRs though!

This application is just stitching together the excellent work of github.com/dustin and github.com/bocajim into a stand-alone application that can talk to a IKEA Trådfri gateway out-of-the-box without any dependencies on libcoap, openssl or similar libraries.

Inspired by:

Changelog

  • 2024-05-20: Update Go version, fix lint errors and other chores.
  • 2024-05-19: Goreleaser support with Github Actions + chores by dvoros
  • 2023-01-30: Update to Go 1.19 by Beaujr
  • 2020-10-11: v1.0.1 release. Power outlet querying by Yuriy Nasretdinov
  • 2020-05-24: v1.0.0 release. GroupIDs and DeviceIDs are now integers. Some minor refactoring after running golang-cilint
  • 2020-05-24: Corrected property names on models, device listing, timed color change support and HSL color support by rubikscraft
  • 2020-02-11: Blinds support by https://github.com/sthorsen
  • 2020-01-16: Updated logging to use logrus with configurable log level in config.json.
  • 2019-06-19: gRPC support by https://github.com/Age15990
  • 2019-06-08: Configurable HTTP port by https://github.com/Mirdinus
  • 2019-04-02: Configuration redone by https://github.com/Hades32
  • 2019-04-01: Fixed issue with -authenticate
  • 2019-03-10: Initial release

Compatibility

tradfri-go has been tested against the following DTLS-enabled COAP servers:

  • IKEA Trådfri Gateway using PSK after token exchange: OK
  • Californicum COAP server on Scandium DTLS: OK

Building

Uses go modules.

export GO111MODULE=on
go build -o tradfri-go

PSK exchange

The Trådfri gateway has its Pre-shared key (PSK) printed on the bottom sticker. However, that PSK is only used for making an initial exchange where you specify a unique Client_id and the original PSK, and you get a new PSK in return that you use for subsequent interactions with the gateway.

tradfri-go supports this operation out of the box using the following command:

> ./tradfri-go --authenticate --client_id=MyCoolID --psk=TheKeyAtTheBottomOfYourGateway --gateway_ip=<ip to your gateway>

The generated new PSK and settings used are stored in the current directory in the file "config.json", e.g:

> cat config.json
{
  "client_id": "MyCoolID",
  "gateway_address": "192.168.1.19:5684",
  "gateway_ip": "192.168.1.19",
  "pre_shared_key": "the generated psk goes here",
  "psk": "the generated psk goes here",
  "loglevel":"info"
}

tradfri-go will try to read config.json when starting up, and will in that case set the required properties accordingly.

If you don't feel like using config.json, you can either specify the configuration as command-line flags or using the following environment variables:

./tradfri-go --server --client_id MyCoolID122 --psk mynewkey --gateway_ip=192.168.1.19

or

> export CLIENT_ID=MyCoolID1122
> export PRE_SHARED_KEY=mynewkey
> export GATEWAY_IP=192.168.1.19

Configuration is resolved in the following order of precedence:

config.json -> command-line arguments -> environment variables

Determine gateway IP

tradfri-go has no means of finding out the IP of the Gateway. I suggest checking your Router's list of connected devices and try to find an item starting with "GW-".

Running in server mode

Server mode connects to your gateway and then publishes a really simple RESTful interface and a gRPC service for querying your gateway or mutating some state on bulbs etc:

./tradfri-go --server

Now, you can use the simple RESTful API provided by tradfri-go which returns more human-readable responses than the raw CoAP responses:

> curl http://localhost:8080/api/device/65538 | jq .
{
  "deviceMetadata": {
    "id": 65538,
    "name": "Färgglad",
    "vendor": "IKEA of Sweden",
    "type": "TRADFRI bulb E27 CWS opal 600lm"
  },
  "dimmer": 100,
  "xcolor": 30015,
  "ycolor": 26870,
  "rgbcolor": "f1e0b5",
  "power": true
}

Or use one of the declarative endpoints to mutate the state of the bulb:

> curl -X PUT -d '{"rgbcolor":"f1e0b5"}' http://localhost:8080/api/device/65538/rgb

Blinds support

tradfri-go now supports controlling IKEA Blinds by passing a positioning value between 0-100.

Examples (curl and gRPC)

> curl -X PUT -d '{"positioning": 20}' http://localhost:8080/api/device/65552/position
> grpcurl -plaintext -d '{"id": 65552, "value": 20}' localhost:8081 grpc_server.TradfriService/ChangeDevicePositioning

Sets the position to 20% extended.

gRPC support

If you want to use the gRPC service, implement your client like this:

var client pb.TradfriServiceClient
{
	conn, err := grpc.Dial("localhost:8081",
		grpc.WithInsecure(),
	)
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	client = pb.NewTradfriServiceClient(conn)
}

while importing pb "github.com/eriklupander/tradfri-go/grpc_server/golang".

And, as simple as calling every other method in your code, request the server like this:

resp, err := client.ListGroups(context.Background(), &pb.ListGroupsRequest{})

You can also install grpcurl and query the server via the command line:

> grpcurl -plaintext localhost:8081 grpc_server.TradfriService/ListGroups
> grpcurl -plaintext -d '{"id": 65552}' localhost:8081 grpc_server.TradfriService/TurnDeviceOn

Just like the client mode, the application will try to use clientId/PSK from psk.key or using env vars.

Running in client mode

Client mode lets you GET and PUT raw coap payloads to your gateway using the "-get" and "-put" args.

A few examples:

GET my bulb at /15001/65538:

./tradfri-go --get /15001/65538
{"9019":1,"9001":"Färgglad","9002":1550336061,"9020":1551721891,"9003":65538,"9054":0,"5750":2,"3":{"0":"IKEA of Sweden","1":"TRADFRI bulb E27 CWS opal 600lm","2":"","3":"1.3.009","6":1},"3311":[{"5708":65279,"5850":1,"5851":100,"5707":53953,"5709":20316,"5710":8520,"5706":"8f2686","9003":0}]}

PUT that turns off the bulb at /15001/65538:

./tradfri-go --put /15001/65538 --payload '{ "3311": [{ "5850": 0 }] }'

PUT that turns on the bulb at /15001/65538 and sets dimmer to 200:

./tradfri-go --put /15001/65538 --payload '{ "3311": [{ "5850": 1, "5851": 200 }] }'

PUT that sets color of the bulb at /15001/65538 to purple and the dimmer to 100:

./tradfri-go --put /15001/65538 --payload '{ "3311": [{ "5706": "8f2686", "5851": 100 }] }'

The colors possible to set on the bulbs varies. The colors are in the CIE 1931 color space whose x/y values in theory can be set using the 5709 and 5710 codes to values between 0 and 65535. You can't set arbitrary values due to how the CIE 1931 (yes, it's a standard from 1931!) works. Play around with the values, I havn't broken my full-color "TRADFRI bulb E27 CWS opal 600lm" yet...

LICENSE

Uses MIT license, see LICENSE

tradfri-go's People

Contributors

beaujr avatar dependabot[bot] avatar dvoros avatar eriklupander avatar hades32 avatar mirdinus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

tradfri-go's Issues

Could not connect to the gateway after firmware update: received fatal alert: unknown(115)

After the firmware update 1.12.31 in the ikea router (the update was installed automatically by the router itself) I could no longer connect to tradfri router using tradfri-go, but IKEA Smart Home app and Google Home integrations still work fine. Weird.

I am always getting the following error when I run any command like the following (the command previously worked fine):

$ ./tradfri-go --put /15001/65537 --payload '{"3312":[{"5850":0,"5851":254,"9003":0}]}'
Using loglevel: info
INFO[2020-11-26T12:28:05Z] Connecting to peer at 192.168.1.120:5684     
WARN[2020-11-26T12:28:05Z] dtls: [udp][[::]:63785] received fatal alert: unknown(115)  fields.level=warn peer="192.168.1.120:5684"
INFO[2020-11-26T12:28:20Z] Unable to connect to Gateway at 192.168.1.120:5684: dtls: timed out waiting for handshake to complete 

I can succesfully ping the device though:

$ ping 192.168.1.120
PING 192.168.1.120 (192.168.1.120): 56 data bytes
64 bytes from 192.168.1.120: icmp_seq=0 ttl=128 time=1.988 ms
64 bytes from 192.168.1.120: icmp_seq=1 ttl=128 time=2.420 ms

I tried restarting the ikea router but it didn't help. Any ideas how to diagnose the issue?

Thanks

dtls timed out

I'm able to use tradfri-go --authenticate to authenticate to the gateway and the config.json file has been created.
But, when trying to start the server I get this error after a couple of seconds:

Running in server mode on :8080
Connecting to peer at <ip:port>
Unable to connect to Gateway at <ip:port>: dtls: timed out waiting for handshake to complete

I'm getting the same error with https://github.com/barnybug/go-tradfri, so the error might be related to the shared dlts library.

Do you know what might cause this?

Vicious circle on first run...

From the readme, this should work:

./tradfri-go -authenticate -clientId blubber -psk secretsecret -gateway 192.168.178.157
Could not find psk.key in current directory, trying to use env-vars CLIENT_ID and PRE_SHARED_KEY instead
panic: Unable to resolve PRE_SHARED_KEY from env-var or psk.key file

But it fails... Looking at the code I also can't see how viper should be able to get the relation between -psk and PRE_SHARED_KEY...

value of power json is always zero

When sending the following line of python to the REST-API of tradfri-go in server-mode the value 1 is ignored. It will always default to 0.
requests.put("http://10.42.3.34:8080/api/device/65539/power", data = {"power":1})

powerRequest := model.PowerRequest{}
err := json.Unmarshal(body, &powerRequest)
res, err := tradfriClient.PutDevicePower(deviceId, powerRequest.Power)

The value of powerRequest.Power is already 0 in line 94. I tested with several ways of input, like a json conversion before sending the data or converting it to a string. I also discovered that this also applies to the dimmer and the dimming and setState function.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.