Skip to content

Commit d353605

Browse files
author
davidgraeff
committed
Scene details shorter, widgets work, listen service fix
* Listen Service fixed * Widgets use a Widget Update Service * ShortcutExecutionActivity updates widgets if necessary * DevicesUpdate Interface for adapters (un/registerDeviceUpdate) * New mechanism for requesting a device update and get informed on that update or get a timeout * New data structure: DeviceCommand. Build from OutletCommandGroup which is good for storing on disk but not for creating the command udp packet. Still missing / bugs: * Save scenes and configured devices as json files * NFC * Testing
1 parent 4e0b0c5 commit d353605

29 files changed

+1151
-694
lines changed

.idea/misc.xml

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/workspace.xml

+392-322
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/src/main/AndroidManifest.xml

+3-4
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
android:name=".shortcut.ShortcutExecutionActivity"
3333
android:label="@string/app_name"
3434
android:icon="@drawable/netpowerctrl"
35-
android:theme="@style/Theme.Transparent"
36-
android:exported="true">
35+
android:launchMode="singleInstance"
36+
android:theme="@style/Theme.Transparent">
3737
<intent-filter>
3838
<action android:name="android.intent.action.MAIN" />
3939
</intent-filter>
@@ -74,11 +74,10 @@
7474
</receiver>
7575

7676
<service
77-
android:label="@string/app_name"
78-
android:icon="@drawable/netpowerctrl"
7977
android:name="oly.netpowerctrl.anelservice.NetpowerctrlService"
8078
android:permission="android.permission.INTERNET" />
8179

80+
<service android:name="oly.netpowerctrl.widget.WidgetUpdateService" />
8281
</application>
8382

8483

Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package oly.netpowerctrl.anelservice;
22

33
/**
4-
* Created by david on 30.12.13.
4+
* on device error
55
*/
66
public interface DeviceError {
7-
void onDeviceError(String devicename, String errMessage);
7+
void onDeviceError(String deviceName, String errMessage);
88
}
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,67 @@
11
package oly.netpowerctrl.anelservice;
22

33
import android.content.Context;
4-
import android.content.SharedPreferences;
5-
import android.preference.PreferenceManager;
6-
import android.widget.Toast;
7-
8-
import org.json.JSONArray;
9-
import org.json.JSONException;
10-
import org.json.JSONObject;
4+
import android.os.Handler;
115

126
import java.net.DatagramPacket;
137
import java.net.DatagramSocket;
148
import java.net.InetAddress;
15-
import java.util.ArrayList;
16-
import java.util.HashSet;
9+
import java.util.Collection;
10+
import java.util.Iterator;
1711

1812
import oly.netpowerctrl.R;
19-
import oly.netpowerctrl.main.NetpowerctrlActivity;
20-
13+
import oly.netpowerctrl.datastructure.DeviceInfo;
14+
import oly.netpowerctrl.main.NetpowerctrlApplication;
15+
import oly.netpowerctrl.preferences.SharedPrefs;
16+
import oly.netpowerctrl.utils.ShowToast;
17+
18+
/**
19+
* Use the static sendQuery and sendBroadcastQuery methods to issue a query to one
20+
* or all devices. If you want to issue a query and get notified on the result or get a
21+
* timeout if no reaction can be received within 1.2s, create a DeviceQuery object with
22+
* all devices to query.
23+
*/
2124
public class DeviceQuery {
25+
private Collection<DeviceInfo> devices_to_observe;
26+
private DeviceUpdateStateOrTimeout target;
27+
private Handler timeoutHandler = new Handler();
28+
private Runnable timeoutRunnable = new Runnable() {
29+
@Override
30+
public void run() {
31+
for (DeviceInfo di : devices_to_observe)
32+
target.onDeviceTimeout(di);
33+
freeSelf();
34+
}
35+
};
36+
37+
public DeviceQuery(Context context, DeviceUpdateStateOrTimeout target, Collection<DeviceInfo> devices_to_observe) {
38+
this.target = target;
39+
this.devices_to_observe = devices_to_observe;
40+
41+
timeoutHandler.postDelayed(timeoutRunnable, 1200);
42+
// Send out broadcast
43+
for (DeviceInfo di : devices_to_observe)
44+
sendQuery(context, di.HostName, di.SendPort);
45+
}
46+
47+
public void notifyAndRemove(DeviceInfo received_data) {
48+
Iterator<DeviceInfo> it = devices_to_observe.iterator();
49+
while (it.hasNext()) {
50+
DeviceInfo device_to_observe = it.next();
51+
if (device_to_observe.equals(received_data)) {
52+
it.remove();
53+
target.onDeviceUpdated(received_data);
54+
}
55+
}
56+
if (devices_to_observe.isEmpty()) {
57+
timeoutHandler.removeCallbacks(timeoutRunnable);
58+
freeSelf();
59+
}
60+
}
61+
62+
private void freeSelf() {
63+
NetpowerctrlApplication.instance.removeUpdateDeviceState(this);
64+
}
2265

2366
public static void sendQuery(final Context context, final String hostname, final int port) {
2467
new Thread(new Runnable() {
@@ -34,79 +77,14 @@ public void run() {
3477
s.send(p);
3578
s.close();
3679
} catch (final Exception e) {
37-
NetpowerctrlActivity._this.runOnUiThread(new Runnable() {
38-
public void run() {
39-
Toast.makeText(context, context.getResources().getString(R.string.error_sending_inquiry) + ": " + e.getMessage(), Toast.LENGTH_LONG).show();
40-
}
41-
});
80+
ShowToast.FromOtherThread(context, context.getResources().getString(R.string.error_sending_inquiry) + ": " + e.getMessage());
4281
}
4382
}
4483
}).start();
4584
}
4685

4786
public static void sendBroadcastQuery(final Context context) {
48-
for (int port : getAllSendPorts(context))
87+
for (int port : SharedPrefs.getAllSendPorts(context))
4988
sendQuery(context, "255.255.255.255", port);
5089
}
51-
52-
53-
public static int getDefaultSendPort(Context context) {
54-
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
55-
int send_udp = context.getResources().getInteger(R.integer.default_send_port);
56-
try {
57-
send_udp = Integer.parseInt(prefs.getString("standard_send_port", ""));
58-
} catch (NumberFormatException e) { /*nop*/ }
59-
return send_udp;
60-
}
61-
62-
public static int getDefaultRecvPort(Context context) {
63-
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
64-
int recv_udp = context.getResources().getInteger(R.integer.default_recv_port);
65-
try {
66-
recv_udp = Integer.parseInt(prefs.getString("standard_recv_port", ""));
67-
} catch (NumberFormatException e) { /*nop*/ }
68-
return recv_udp;
69-
}
70-
71-
//! get a list of all send ports of all configured devices plus the default send port
72-
private static ArrayList<Integer> getAllSendPorts(Context context) {
73-
HashSet<Integer> ports = new HashSet<Integer>();
74-
ports.add(getDefaultSendPort(context));
75-
76-
SharedPreferences prefs = context.getSharedPreferences("oly.netpowerctrl", Context.MODE_PRIVATE);
77-
String configured_devices_str = prefs.getString("configured_devices", "[]");
78-
try {
79-
JSONArray jdevices = new JSONArray(configured_devices_str);
80-
for (int i = 0; i < jdevices.length(); i++) {
81-
JSONObject jhost = jdevices.getJSONObject(i);
82-
if (!jhost.getBoolean("default_ports"))
83-
ports.add(jhost.getInt("sendport"));
84-
}
85-
} catch (JSONException e) {
86-
// nop
87-
}
88-
89-
return new ArrayList<Integer>(ports);
90-
}
91-
92-
//! get a list of all receive ports of all configured devices plus the default receive port
93-
public static ArrayList<Integer> getAllReceivePorts(Context context) {
94-
HashSet<Integer> ports = new HashSet<Integer>();
95-
ports.add(getDefaultRecvPort(context));
96-
97-
SharedPreferences prefs = context.getSharedPreferences("oly.netpowerctrl", Context.MODE_PRIVATE);
98-
String configured_devices_str = prefs.getString("configured_devices", "[]");
99-
try {
100-
JSONArray jdevices = new JSONArray(configured_devices_str);
101-
for (int i = 0; i < jdevices.length(); i++) {
102-
JSONObject jhost = jdevices.getJSONObject(i);
103-
if (!jhost.getBoolean("default_ports"))
104-
ports.add(jhost.getInt("recvport"));
105-
}
106-
} catch (JSONException e) {
107-
// nop
108-
}
109-
110-
return new ArrayList<Integer>(ports);
111-
}
11290
}

app/src/main/java/oly/netpowerctrl/anelservice/DeviceSend.java

+30-99
Original file line numberDiff line numberDiff line change
@@ -1,131 +1,51 @@
11
package oly.netpowerctrl.anelservice;
22

33
import android.content.Context;
4-
import android.util.Log;
54

65
import java.io.IOException;
76
import java.net.DatagramPacket;
87
import java.net.DatagramSocket;
98
import java.net.InetAddress;
10-
import java.net.UnknownHostException;
9+
import java.util.Collection;
1110
import java.util.Locale;
12-
import java.util.Map;
13-
import java.util.TreeMap;
1411

1512
import oly.netpowerctrl.R;
13+
import oly.netpowerctrl.datastructure.DeviceCommand;
1614
import oly.netpowerctrl.datastructure.DeviceInfo;
17-
import oly.netpowerctrl.datastructure.OutletCommand;
18-
import oly.netpowerctrl.datastructure.OutletCommandGroup;
1915
import oly.netpowerctrl.utils.ShowToast;
2016

2117
public class DeviceSend {
22-
/**
23-
* This class extracts the destination ip, user access data
24-
* and provides outlet manipulation methods (on/off/toggle).
25-
* The resulting data byte variable can be used to send a
26-
* bulk-change udp packet.
27-
*/
28-
static final public class DeviceSwitch {
29-
// build bulk change byte, see: www.anel-elektronik.de/forum_neu/viewtopic.php?f=16&t=207
30-
// “Sw” + Steckdosen + User + Passwort
31-
// Steckdosen = Zustand aller Steckdosen binär
32-
// LSB = Steckdose 1, MSB (Bit 8)= Steckdose 8 (PRO, POWER), Bit 2 = Steckdose 3 (HOME).
33-
// Soll nur 1 & 5 eingeschaltet werden=00010001 = 17 = 0x11 (HEX)
34-
private byte data;
35-
InetAddress dest;
36-
int port;
37-
String access;
38-
39-
public DeviceSwitch(DeviceInfo di) {
40-
this.data = 0;
41-
this.port = di.SendPort;
42-
try {
43-
this.dest = InetAddress.getByName(di.HostName);
44-
} catch (UnknownHostException e) {
45-
this.dest = null;
46-
}
47-
this.access = di.UserName + di.Password;
48-
for (int i = 0; i < di.Outlets.size(); ++i) {
49-
Log.w("DeviceSwitch", Integer.valueOf(i).toString() + " " + di.DeviceName + " " + Integer.valueOf(di.Outlets.get(i).OutletNumber).toString() + " " + Boolean.valueOf(di.Outlets.get(i).State).toString());
50-
51-
if (!di.Outlets.get(i).Disabled && di.Outlets.get(i).State)
52-
switchOn(di.Outlets.get(i).OutletNumber);
53-
}
54-
55-
}
56-
57-
void switchOn(int outletNumber) {
58-
data |= ((byte) (1 << outletNumber - 1));
59-
}
60-
61-
void switchOff(int outletNumber) {
62-
data &= ~((byte) (1 << outletNumber - 1));
63-
}
64-
65-
void toggle(int outletNumber) {
66-
if ((data & ((byte) (1 << outletNumber - 1))) > 0) {
67-
switchOff(outletNumber);
68-
} else {
69-
switchOn(outletNumber);
70-
}
71-
}
72-
73-
byte getSwitchByte() {
74-
return data;
75-
}
76-
}
77-
7818
/**
7919
* Bulk version of sendOutlet. Send changes for each device in only one packet per device.
8020
*
8121
* @param context The context of the activity for showing toast messages and
8222
* getResources
83-
* @param og The OutletCommandGroup
84-
* @return Return true if all fields have been found
23+
* @param device_commands Bulk command per device
8524
*/
86-
static public void sendOutlet(final Context context, final OutletCommandGroup og) {
25+
static public void sendOutlet(final Context context, final Collection<DeviceCommand> device_commands, final boolean requestNewValuesAfterSend) {
8726
// udp sending in own thread
8827
new Thread(new Runnable() {
8928
public void run() {
9029
try {
9130
DatagramSocket s = new DatagramSocket();
9231

93-
TreeMap<String, DeviceSwitch> devices = new TreeMap<String, DeviceSwitch>();
94-
for (OutletCommand c : og.commands) {
95-
if (!devices.containsKey(c.device_mac)) {
96-
devices.put(c.device_mac, new DeviceSwitch(c.outletinfo.device));
97-
}
98-
99-
//Log.w("sendOutlet",c.device_mac+" "+ Integer.valueOf(c.outletNumber).toString()+" "+Integer.valueOf(c.state).toString());
100-
101-
switch (c.state) {
102-
case 0:
103-
devices.get(c.device_mac).switchOn(c.outletNumber);
104-
break;
105-
case 1:
106-
devices.get(c.device_mac).switchOff(c.outletNumber);
107-
break;
108-
case 2:
109-
devices.get(c.device_mac).toggle(c.outletNumber);
110-
break;
111-
}
112-
113-
}
114-
115-
for (Map.Entry<String, DeviceSwitch> c : devices.entrySet()) {
116-
sendAllOutlets(c.getValue(), s);
32+
for (DeviceCommand c : device_commands) {
33+
sendAllOutlets(c, s);
11734
}
11835
s.close();
11936

120-
// wait 100ms
121-
try {
122-
Thread.sleep(100);
123-
} catch (InterruptedException ignored) {
124-
}
37+
if (requestNewValuesAfterSend) {
38+
// wait 100ms
39+
try {
40+
Thread.sleep(100);
41+
} catch (InterruptedException ignored) {
42+
}
12543

126-
// request new values from each device
127-
for (Map.Entry<String, DeviceSwitch> device : devices.entrySet()) {
128-
DeviceQuery.sendQuery(context, device.getValue().dest.getHostAddress(), device.getValue().port);
44+
// request new values from each device
45+
46+
for (DeviceCommand device_command : device_commands) {
47+
DeviceQuery.sendQuery(context, device_command.dest.getHostAddress(), device_command.port);
48+
}
12949
}
13050

13151
} catch (final IOException e) {
@@ -142,14 +62,25 @@ public void run() {
14262
* @param context The context
14363
* @param device The device and state
14464
*/
145-
static public void sendAllOutlets(final Context context, final DeviceSwitch device) {
65+
static public void sendAllOutlets(final Context context, final DeviceCommand device, final boolean requestNewValuesAfterSend) {
14666
// udp sending in own thread
14767
new Thread(new Runnable() {
14868
public void run() {
14969
try {
15070
DatagramSocket s = new DatagramSocket();
15171
sendAllOutlets(device, s);
15272
s.close();
73+
74+
if (requestNewValuesAfterSend) {
75+
// wait 100ms
76+
try {
77+
Thread.sleep(100);
78+
} catch (InterruptedException ignored) {
79+
}
80+
81+
// request new values
82+
DeviceQuery.sendQuery(context, device.dest.getHostAddress(), device.port);
83+
}
15384
} catch (final IOException e) {
15485
ShowToast.FromOtherThread(context, context.getResources().getString(R.string.error_sending_inquiry) + ": "
15586
+ e.getMessage());
@@ -158,7 +89,7 @@ public void run() {
15889
}).start();
15990
}
16091

161-
static private void sendAllOutlets(final DeviceSwitch device, DatagramSocket s) throws IOException {
92+
static private void sendAllOutlets(final DeviceCommand device, DatagramSocket s) throws IOException {
16293
if (device.dest != null) {
16394
byte[] data = new byte[3 + device.access.length()];
16495
data[0] = 'S';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package oly.netpowerctrl.anelservice;
2+
3+
import oly.netpowerctrl.datastructure.DeviceInfo;
4+
5+
public interface DeviceUpdate {
6+
void onDeviceUpdated(DeviceInfo di);
7+
}

0 commit comments

Comments
 (0)