Skip to content

Commit 44052a3

Browse files
r41dhardillb
authored andcommitted
Implement color changing (#3)
* Make things a lot more readably by including Tradfri constants * Add color temperature constants * Fix 2 bugs I unfortunately introduced * Fix exception if a bulb has no power on lightbulb socket * Implement color changing * Add MQTT example to simplify to get started * Ensure that dimmer input value is in range * version bump because of color changing feature * Change color to temperature because RGB bulbs may be released in the future * Revise Tradfri Constants
1 parent a64ceb9 commit 44052a3

File tree

4 files changed

+135
-34
lines changed

4 files changed

+135
-34
lines changed

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
java -jar TRADFRI2MQTT-0.0.2-SNAPSHOT.jar -ip [gateway ip] -psk [gateway secret] -broker [mqtt broker url]
1+
# Invocation
2+
3+
java -jar TRADFRI2MQTT-X.X.X-SNAPSHOT.jar -ip [gateway ip] -psk [gateway secret] -broker [mqtt broker url]
24

35
e.g.
46

5-
`java -jar TRADFRI2MQTT-0.0.2-SNAPSHOT.jar -ip 192.168.1.111 -psk xxxxxxxxxxxxxxxx -broker tcp://localhost`
7+
`java -jar TRADFRI2MQTT-X.X.X-SNAPSHOT.jar -ip 192.168.1.XXX -psk xxxxxxxxxxxxxxxx -broker tcp://localhost`
68

79
Publishes state messages on topics like this:
810

911
- TRÅDFRI/bulb/Living Room Light/state/on
1012
- TRÅDFRI/bulb/Living Room Light/state/dim
13+
- TRÅDFRI/bulb/Living Room Light/state/temperature
1114
- TRÅDFRI/room/Living Room/state/on
1215
- TRÅDFRI/room/Living Room/state/dim
1316

@@ -16,10 +19,22 @@ Subscribes to control messages on topics like this:
1619

1720
- TRÅDFRI/bulb/Living Room Light/control/on
1821
- TRÅDFRI/bulb/Living Room Light/control/dim
22+
- TRÅDFRI/bulb/Living Room Light/control/temperature
1923
- TRÅDFRI/room/Living Room/control/on
2024
- TRÅDFRI/room/Living Room/control/dim
2125

2226
publish 0/1 to the `on` topic to turn the light off/on respectively
2327

2428
publish 0-254 to the `dim` topic to change the brightness
2529

30+
publish "cold" / "normal" / "warm" to the `temperature` topic to change temperatures. this only works on bulbs
31+
32+
# MQTT broker example
33+
An easy-to-use MQTT broker is [mosquitto](https://mosquitto.org/).
34+
35+
After installation run it locally with `mosquitto`.
36+
37+
Then submit commands like this:
38+
`mosquitto_pub -t "TRÅDFRI/bulb/LivingRoomBulb1/control/temperature" -m warm`
39+
or subscribe like this:
40+
`mosquitto_sub -t "TRÅDFRI/room/LivingRoom/state/on"`

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>uk.me.hardill</groupId>
66
<artifactId>TRADFRI2MQTT</artifactId>
7-
<version>0.0.2-SNAPSHOT</version>
7+
<version>0.0.3-SNAPSHOT</version>
88
<packaging>jar</packaging>
99

1010
<name>TRADFRI2MQTT</name>

src/main/java/uk/me/hardill/TRADFRI2MQTT/Main.java

Lines changed: 69 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
*/
44
package uk.me.hardill.TRADFRI2MQTT;
55

6+
import static uk.me.hardill.TRADFRI2MQTT.TradfriConstants.*;
7+
68
import java.net.InetSocketAddress;
79
import java.net.URI;
810
import java.net.URISyntaxException;
@@ -88,36 +90,51 @@ public void messageArrived(String topic, MqttMessage message) throws Exception {
8890
System.out.println(command);
8991
try{
9092
JSONObject json = new JSONObject();
91-
if (bulb) {
93+
if (bulb) { // single bulb
9294
JSONObject settings = new JSONObject();
9395
JSONArray array = new JSONArray();
9496
array.put(settings);
95-
json.put("3311", array);
97+
json.put(LIGHT, array);
9698
if (command.equals("dim")) {
97-
settings.put("5851", Integer.parseInt(message.toString()));
98-
settings.put("5712", 3); // second transition
99+
settings.put(DIMMER, Math.min(DIMMER_MAX, Math.max(DIMMER_MIN, Integer.parseInt(message.toString()))));
100+
settings.put(TRANSITION_TIME, 3); // transition in seconds
101+
} else if (command.equals("temperature")) {
102+
// not sure what the COLOR_X and COLOR_Y values do, it works without them...
103+
switch (message.toString()) {
104+
case "cold":
105+
settings.put(COLOR, COLOR_COLD);
106+
break;
107+
case "normal":
108+
settings.put(COLOR, COLOR_NORMAL);
109+
break;
110+
case "warm":
111+
settings.put(COLOR, COLOR_WARM);
112+
break;
113+
default:
114+
System.err.println("Invalid temperature supplied: " + message.toString());
115+
}
99116
} else if (command.equals("on")) {
100117
if (message.toString().equals("0")) {
101-
settings.put("5850", 0);
118+
settings.put(ONOFF, 0);
102119
} else {
103-
settings.put("5850", 1);
120+
settings.put(ONOFF, 1);
104121
}
105122
}
106123
String payload = json.toString();
107-
Main.this.set("coaps://" + ip + "//15001/" + id, payload);
108-
} else {
124+
Main.this.set("coaps://" + ip + "//" + DEVICES + "/" + id, payload);
125+
} else { // whole group
109126
if (command.equals("dim")) {
110-
json.put("5851", Integer.parseInt(message.toString()));
111-
json.put("5712", 3);
127+
json.put(DIMMER, Integer.parseInt(message.toString()));
128+
json.put(TRANSITION_TIME, 3);
112129
} else {
113130
if (message.toString().equals("0")) {
114-
json.put("5850", 0);
131+
json.put(ONOFF, 0);
115132
} else {
116-
json.put("5850", 1);
133+
json.put(ONOFF, 1);
117134
}
118135
}
119136
String payload = json.toString();
120-
Main.this.set("coaps://" + ip + "//15004/" + id, payload);
137+
Main.this.set("coaps://" + ip + "//" + GROUPS + "/" + id, payload);
121138
}
122139
} catch (Exception e) {
123140
e.printStackTrace();
@@ -158,13 +175,13 @@ public void run() {
158175
private void discover() {
159176
//bulbs
160177
try {
161-
URI uri = new URI("coaps://" + ip + "//15001");
178+
URI uri = new URI("coaps://" + ip + "//" + DEVICES);
162179
CoapClient client = new CoapClient(uri);
163180
client.setEndpoint(endPoint);
164181
CoapResponse response = client.get();
165182
JSONArray array = new JSONArray(response.getResponseText());
166183
for (int i=0; i<array.length(); i++) {
167-
String devUri = "coaps://"+ ip + "//15001/" + array.getInt(i);
184+
String devUri = "coaps://" + ip + "//" + DEVICES + "/" + array.getInt(i);
168185
this.watch(devUri);
169186
}
170187
client.shutdown();
@@ -177,13 +194,13 @@ private void discover() {
177194
}
178195

179196
try {
180-
URI uri = new URI("coaps://" + ip + "//15004");
197+
URI uri = new URI("coaps://" + ip + "//" + GROUPS);
181198
CoapClient client = new CoapClient(uri);
182199
client.setEndpoint(endPoint);
183200
CoapResponse response = client.get();
184201
JSONArray array = new JSONArray(response.getResponseText());
185202
for (int i=0; i<array.length(); i++) {
186-
String devUri = "coaps://"+ ip + "//15004/" + array.getInt(i);
203+
String devUri = "coaps://" + ip + "//" + GROUPS + "/" + array.getInt(i);
187204
this.watch(devUri);
188205
}
189206
client.shutdown();
@@ -232,41 +249,62 @@ public void onLoad(CoapResponse response) {
232249
System.out.println(response.getOptions().toString());
233250
try {
234251
JSONObject json = new JSONObject(response.getResponseText());
235-
//TODO change this test to someting based on 5750 values
252+
//TODO change this test to something based on 5750 values
236253
// 2 = light?
237254
// 0 = remote/dimmer?
238-
if (json.has("3311") && (json.has("5750") && json.getInt("5750") == 2)){
255+
if (json.has(LIGHT) && (json.has(TYPE) && json.getInt(TYPE) == 2)) { // single bulb
239256
MqttMessage message = new MqttMessage();
240-
int state = json.getJSONArray("3311").getJSONObject(0).getInt("5850");
257+
// A 'JSONObject["5850"] not found' exception occurs if there is a registered lamp with no power
258+
int state;
259+
try {
260+
state = json.getJSONArray(LIGHT).getJSONObject(0).getInt(ONOFF);
261+
} catch (JSONException e) {
262+
System.err.println("Bulb '" + json.getString(NAME) + "' has no power on lightbulb socket");
263+
return; // skip this lamp for now
264+
}
265+
String topic = "TRÅDFRI/bulb/" + json.getString(NAME) + "/state/on";
266+
String topic2 = "TRÅDFRI/bulb/" + json.getString(NAME) + "/state/dim";
267+
String topic3 = "TRÅDFRI/bulb/" + json.getString(NAME) + "/state/temperature";
268+
269+
name2id.put(json.getString(NAME), json.getInt(INSTANCE_ID));
270+
241271
message.setPayload(Integer.toString(state).getBytes());
242272
// message.setRetained(true);
243-
String topic = "TRÅDFRI/bulb/" + json.getString("9001") + "/state/on";
244-
String topic2 = "TRÅDFRI/bulb/" + json.getString("9001") + "/state/dim";
245-
name2id.put(json.getString("9001"), json.getInt("9003"));
273+
246274
MqttMessage message2 = new MqttMessage();
247-
int dim = json.getJSONArray("3311").getJSONObject(0).getInt("5851");
275+
int dim = json.getJSONArray(LIGHT).getJSONObject(0).getInt(DIMMER);
248276
message2.setPayload(Integer.toString(dim).getBytes());
249277
// message2.setRetained(true);
278+
279+
MqttMessage message3 = new MqttMessage();
280+
String temperature = json.getJSONArray(LIGHT).getJSONObject(0).getString(COLOR);
281+
message3.setPayload(temperature.getBytes());
282+
// message3.setRetained(true);
283+
250284
try {
251285
mqttClient.publish(topic, message);
252286
mqttClient.publish(topic2, message2);
287+
mqttClient.publish(topic3, message3);
253288
} catch (MqttException e) {
254289
// TODO Auto-generated catch block
255290
e.printStackTrace();
256291
}
257-
} else if (json.has("9018")) {
292+
} else if (json.has(HS_ACCESSORY_LINK)) { // groups have this entry
258293
//room?
259294
System.out.println("room");
260-
name2id.put(json.getString("9001"), json.getInt("9003"));
295+
name2id.put(json.getString(NAME), json.getInt(INSTANCE_ID));
296+
297+
String topic = "TRÅDFRI/room/" + json.getString(NAME) + "/state/on";
298+
String topic2 = "TRÅDFRI/room/" + json.getString(NAME) + "/state/dim";
299+
261300
MqttMessage message = new MqttMessage();
262-
int state = json.getInt("5850");
301+
int state = json.getInt(ONOFF);
263302
message.setPayload(Integer.toString(state).getBytes());
264-
String topic = "TRÅDFRI/room/" + json.getString("9001") + "/state/on";
265-
String topic2 = "TRÅDFRI/room/" + json.getString("9001") + "/state/dim";
303+
266304
MqttMessage message2 = new MqttMessage();
267-
int dim = json.getInt("5851");
305+
int dim = json.getInt(DIMMER);
268306
message2.setPayload(Integer.toString(dim).getBytes());
269-
307+
270308
try {
271309
mqttClient.publish(topic, message);
272310
mqttClient.publish(topic2, message2);
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package uk.me.hardill.TRADFRI2MQTT;
2+
3+
/**
4+
* Partly inspired by "com/ikea/tradfri/lighting/ipso/IPSOObjects.java"
5+
* Some of these values can be found in the official LwM2M registry:
6+
* http://www.openmobilealliance.org/wp/OMNA/LwM2M/LwM2MRegistry.html
7+
*
8+
* @author r41d
9+
*/
10+
public class TradfriConstants {
11+
12+
// Device types (contained in INSTANCE_ID = "9003")
13+
public static final int TYPE_REMOTE = 0;
14+
public static final int TYPE_BULB = 2;
15+
// The others need to be figured out by people who own these
16+
17+
// Top level navigation
18+
public static final String DEVICES = "15001";
19+
public static final String GROUPS = "15004";
20+
21+
// Values in JSON data
22+
public static final String NAME = "9001"; // used in both devices and groups
23+
public static final String INSTANCE_ID = "9003"; // In devices: device ID. In groups: list of device IDs
24+
public static final String HS_ACCESSORY_LINK = "9018";
25+
public static final String LIGHT = "3311"; // urn:oma:lwm2m:ext:3311 in LwM2M registry
26+
public static final String TYPE = "5750"; // "Application Type" in LwM2M registry
27+
public static final String ONOFF = "5850"; // "On/Off" in LwM2M registry
28+
public static final String DIMMER = "5851"; // "Dimmer" in LwM2M registry
29+
public static final String TRANSITION_TIME = "5712"; // not contained in LwM2M registry
30+
31+
// Color / Temperature related, these are independent of brightness, i.e. do not change if brightness does
32+
public static final String COLOR = "5706";
33+
public static final String COLOR_X = "5709";
34+
public static final String COLOR_Y = "5710";
35+
public static final String COLOR_COLD = "f5faf6";
36+
public static final String COLOR_COLD_X = "24930";
37+
public static final String COLOR_COLD_Y = "24694";
38+
public static final String COLOR_NORMAL = "f1e0b5";
39+
public static final String COLOR_NORMAL_X = "30140";
40+
public static final String COLOR_NORMAL_Y = "26909";
41+
public static final String COLOR_WARM = "efd275";
42+
public static final String COLOR_WARM_X = "33135";
43+
public static final String COLOR_WARM_Y = "27211";
44+
45+
// Dimmer related
46+
public static final int DIMMER_MIN = 0;
47+
public static final int DIMMER_MAX = 254;
48+
}

0 commit comments

Comments
 (0)