Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 49 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,7 @@
# ikea-smartlight
# ikea-smartlights
python framework for controlling the Ikea smart lights (tradfri)

### update
as of gateway version 1.1.15 the usage of securityid is prohibated, you need to register a api user and you will get a pre shared key from the gateway. follow the steps below and all should be well
```bash
coap-client -m post -u "Client_identity" -k "SECURITY_CODE" -e '{"9090":"IDENTITY"}' "coaps://IP_ADDRESS:5684/15011/9063"
# SECURITY_CODE = the security code under the gateway
# IDENTITY = your api user
```
when this is done create a file called tradfri.cfg and add
```ini
[tradfri]
hubip = x.x.x.x
apiuser = username
apikey = pre shared key
```
getting the apple HomeKit code for your gateway. first ensure you have the pre shared key.
```bash
coap-client -m get -u "IDENTITY" -k "PRE SHARED KEY" "coaps://IP_ADDRESS:5684/15011/15012" 2> /dev/null
# Apple HomeKit code looks like: { ... 9083: XXX-XX-XXX, ...}
# XXX-XX-XXX is your HomeKit code
```
## setup

### requirements
at this moment there is no coap libs with dTLS, the ikea smart lights are using dTLS with coap for security. the only option is to build a new libcoap with dTLS included. libcoap requires `cunit, a2x, doxygen and dot` you need to install these requirements first.
Expand All @@ -37,24 +18,16 @@ sudo make install

the framework also requires `tqdm` for showing progressbars, you could strip it from the sourcecode or install the module for python: `pip install pip --upgrade && pip install tqdm`.

### libcoap usage
```bash
# getting tradfri pre shared key
coap-client -m post -u "Client_identity" -k "<key>" -e '{"9090":"IDENTITY"}' "coaps://<hub>:5684/15011/9063"
# getting tradfri information
./coap-client -m get -u "IDENTITY" -k "<psk>" "coaps://<hup>:5684/15001"
# getting tradfri lightbulb status
./coap-client -m get -u "IDENTITY" -k "<psk>" "coaps://<hup>:5684/15001/65537"
### authentication
you will need to authenticate the api before you first use it. to do this, run `python tradfri-authenticate.py` and enter the ip of your hub and the security code (on the back of the hub). the script will automaticaly create a configuration file containing the api key

# turn on tradfri lightbulb
./coap-client -m put -u "IDENTITY" -k "<psk>" -e '{ "3311" : [{ "5850" : 1 }] }' "coaps://<hup>:5684/15001/65537"
# turn off tradfri lightbulb
./coap-client -m put -u "IDENTITY" -k "<psk>" -e '{ "3311" : [{ "5850" : 0 }] }' "coaps://<hup>:5684/15001/65537"
```
## usage

### output
### status
```
python tradfri-status.py
```
```
./tradfri-status.py
[ ] tradfri: requireing all tradfri devices, please wait ...
tradfri lightbulbs: 100%|█████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 10.93 lightbulb/s]
tradfri groups: 100%|█████████████████████████████████████████████████████████████████| 4/4 [00:00<00:00, 10.50 group/s]
Expand All @@ -76,15 +49,53 @@ groupid: 140387, name: woonkamer, state: off
groupid: 186970, name: hal boven, state: off
```

### todo
### turn on a light
```
python tradfri-lights.py -a power -l {LIGHTBULB_ID} -v on
```

### dim the light
```
python tradfri-lights.py -a brightness -l {LIGHTBULB_ID} -v 50
```

### turn off all ligths in room
```
python tradfri-groups.py -a power -g {GROUP_ID} -v off
```

### libcoap usage
```bash
# getting tradfri pre shared key
coap-client -m post -u "Client_identity" -k "<key>" -e '{"9090":"IDENTITY"}' "coaps://<hub>:5684/15011/9063"
# getting tradfri information
./coap-client -m get -u "IDENTITY" -k "<psk>" "coaps://<hup>:5684/15001"
# getting tradfri lightbulb status
./coap-client -m get -u "IDENTITY" -k "<psk>" "coaps://<hup>:5684/15001/65537"

# turn on tradfri lightbulb
./coap-client -m put -u "IDENTITY" -k "<psk>" -e '{ "3311" : [{ "5850" : 1 }] }' "coaps://<hup>:5684/15001/65537"
# turn off tradfri lightbulb
./coap-client -m put -u "IDENTITY" -k "<psk>" -e '{ "3311" : [{ "5850" : 0 }] }' "coaps://<hup>:5684/15001/65537"
```

## HomeKit code
getting the apple HomeKit code for your gateway. first ensure you have the pre shared key.
```bash
coap-client -m get -u "IDENTITY" -k "PRE SHARED KEY" "coaps://IP_ADDRESS:5684/15011/15012" 2> /dev/null
# Apple HomeKit code looks like: { ... 9083: XXX-XX-XXX, ...}
# XXX-XX-XXX is your HomeKit code
```

## todo
- [ ] add support for new color lightbulbs
- [X] add change state (power on/off lightbulb)
- [X] add dimmer value (dimm lightbulb)
- [X] add change state group (power on/off groups)
- [X] add dimmer value group (dimm group)
- [X] add color temperature lightbulb (switch to cold, normal or warm)

### licensing and credits
## licensing and credits
ikea-smartlight is licensed under the GPLv3:
```
This program is free software: you can redistribute it and/or modify
Expand Down
60 changes: 60 additions & 0 deletions tradfri-authenticate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env python

# file : tradfri-authenticate.py
# purpose : authenticate api user and generate configuration file
#
# author : maltejur
# date : 2020/10/24

"""
tradfri-authenticate.py - authenticate api user and generate configuration file

This module requires libcoap with dTLS compiled, at this moment there is no python coap module
that supports coap with dTLS. see ../bin/README how to compile libcoap with dTLS support
"""

# pylint convention disablement:
# C0103 -> invalid-name
# C0200 -> consider-using-enumerate
# pylint: disable=C0200, C0103

from __future__ import print_function
from __future__ import unicode_literals

import os
import sys
import time
import ConfigParser

from tradfri import tradfriActions

def main():
""" main function """
conf = ConfigParser.ConfigParser()
script_dir = os.path.dirname(os.path.realpath(__file__))
conf.read(script_dir + '/tradfri.cfg')

hubip = raw_input("\nhub ip:\t\t")
securityCode = raw_input("security code: \t")

print("\n[ ] acquiring api key ...", end="")

apiuser, apikey = tradfriActions.tradfri_authenticate(hubip, securityCode)

print("\r[+]\n\nuser:\t{}\napikey:\t{}\n".format(apiuser, apikey))

print("[ ] writing configuration file ...", end="")

conf.add_section('tradfri')
conf.set("tradfri", "hubip", hubip)
conf.set("tradfri", "apiuser", apiuser)
conf.set("tradfri", "apikey", apikey)
conf.write(open(script_dir + '/tradfri.cfg','w'))

print("\r[+]\n")

print("all done!\n")

if __name__ == "__main__":
main()
sys.exit(0)
12 changes: 10 additions & 2 deletions tradfri-lights.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import sys
import ConfigParser
import argparse
from textwrap import wrap

from tradfri import tradfriActions

Expand Down Expand Up @@ -72,10 +73,17 @@ def main():
sys.stderr.write('[-] Tradfri: dim value can only be between 1 and 100\n')
sys.exit(1)
elif args.action == 'color':
if args.value == 'warm' or args.value == 'normal' or args.value == 'cold':
if args.value in tradfriActions.get_color_dict().keys():
tradfriActions.tradfri_color_light(hubip, apiuser, apikey, args.lightbulbid, args.value)
else:
sys.stderr.write('[-] Tradfri: color value can only be warm/normal/cold\n')
message = '[-] Tradfri: color value can only be: '
for color in tradfriActions.get_color_dict().keys():
if ' ' in color:
message += " '%s'," % color
else:
message += " %s," % color

sys.stderr.write("\n".join(wrap(message)))
sys.exit(1)

if __name__ == "__main__":
Expand Down
68 changes: 61 additions & 7 deletions tradfri/tradfriActions.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@

import sys
import os
import json
import random
from tradfriStatus import tradfri_get_lightbulb

global coap
coap = '/usr/local/bin/coap-client'
Expand Down Expand Up @@ -70,17 +73,23 @@ def tradfri_dim_light(hubip, apiuser, apikey, lightbulbid, value):
def tradfri_color_light(hubip, apiuser, apikey, lightbulbid, value):
""" function for color temperature tradfri lightbulb """
tradfriHub = 'coaps://{}:5684/15001/{}'.format(hubip, lightbulbid)
payload = None
colors = get_color_dict()

if value in ['warm', 'normal', 'cold']:
payload = '{ "3311" : [{ "5706" : "%s"}] }' % (colors[value])

if payload is None:
color_supported = 'CWS' in tradfri_get_lightbulb(hubip, apiuser, apikey, lightbulbid)[u'3'][u'1']

if value == 'warm':
payload = '{ "3311" : [{ "5709" : %s, "5710": %s }] }' % ("33135", "27211")
elif value == 'normal':
payload = '{ "3311" : [{ "5709" : %s, "5710": %s }] }' % ("30140", "26909")
elif value == 'cold':
payload = '{ "3311" : [{ "5709" : %s, "5710": %s }] }' % ("24930", "24684")
if not color_supported:
print("Your lamp does not support colors.")
sys.exit(1)

payload = '{ "3311" : [{ "5706" : "%s"}] }' % (colors[value])

api = '{} -m put -u "{}" -k "{}" -e \'{}\' "{}"'.format(coap, apiuser, apikey,
payload, tradfriHub)

if os.path.exists(coap):
result = os.popen(api)
else:
Expand Down Expand Up @@ -126,3 +135,48 @@ def tradfri_dim_group(hubip, apiuser, apikey, groupid, value):
sys.exit(1)

return result

def tradfri_authenticate(hubip, securitycode, apiuser = "TRADFRI_PY_API_" + str(random.randint(0, 1000)) ):
""" function for authenticating tradfri and getting apikey """
tradfriHub = 'coaps://{}:5684/15011/9063'.format(hubip)
payload = '{"9090":"' + apiuser + '"}'

api = '{} -m post -u "Client_identity" -k "{}" -e \'{}\' "{}"'.format(coap, securitycode,
payload, tradfriHub)

if os.path.exists(coap):
result = os.popen(api)
else:
sys.stderr.write('[-] libcoap: could not find libcoap\n')
sys.exit(1)

try:
apikey = json.loads(result.read().strip('\n').split('\n')[-1])["9091"]
except ValueError:
raise Exception("Didn't receive valid apikey. This is because the api isn't reachable or the api user already exists.")

return apiuser,apikey

def get_color_dict():
return {
'blue' : '4a418a',
'light blue' : '6c83ba',
'saturated purple' : '8f2686',
'lime' : 'a9d62b',
'light purple': 'c984bb',
'yellow' : 'd6e44b',
'saturated pink' : 'd9337c',
'dark peach' : 'da5d41',
'saturated red' : 'dc4b31',
'cold sky' : 'dcf0f8',
'pink' : 'e491af',
'peach' : 'e57345',
'warm amber' : 'e78834',
'light pink' : 'e8bedd',
'cool daylight' : 'eaf6fb',
'candlelight' : 'ebb63e',
'warm' : 'efd275',
'normal' : 'f1e0b5',
'sunrise' : 'f2eccf',
'cold' : 'f5faf6',
}