Skip to content

Commit f81aff3

Browse files
committed
Added native Java WoL packet.
Updated readme. Add unix startup service instructions and template. Added back devices.json Signed-off-by: elmodo7 <elmodo7yt@gmail.com>
1 parent 9ffb7c8 commit f81aff3

File tree

10 files changed

+233
-9
lines changed

10 files changed

+233
-9
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ target/
33
!.mvn/wrapper/maven-wrapper.jar
44
!**/src/main/**/target/
55
!**/src/test/**/target/
6-
src/main/resources/devices.json
76

87
### STS ###
98
.apt_generated

README.md

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11

2+
23
# Wake on LAN on steroids for your HomeLab
34
![enter image description here](https://media.licdn.com/dms/image/v2/D4E12AQF6AW53XmHJKQ/article-inline_image-shrink_1500_2232/article-inline_image-shrink_1500_2232/0/1726568056394?e=1732147200&v=beta&t=ewBC8EpUBnNUM3v8IaNtCi9PfXQsgsXlUNNOoI1CrhI)
45

@@ -48,19 +49,22 @@ While **cloud** is getting more and more common, having solutions for us that we
4849

4950
### What we need
5051

51-
- A **Linux** based computer (I am using an [ODROID-XU4](https://wiki.odroid.com/odroid-xu4/odroid-xu4))
52-
53-
- **Wakeonlan** packet installed
54-
52+
- **~~Linux\*~~** Any computer capable of running Java (I am using an [ODROID-XU4](https://wiki.odroid.com/odroid-xu4/odroid-xu4))
53+
5554
- [Java JDK/JRE 1.8 or above](https://www.oracle.com/es/java/technologies/javase/javase8u211-later-archive-downloads.html)
55+
56+
> \* Since version 0.1.2 a *Windows* computer should also be able to send WoL packets using this tool.
57+
5658

5759

5860

5961

6062
### Recommended but not mandatory
6163

6264
- [IntelliJ IDEA](https://www.jetbrains.com/idea/)
63-
65+
66+
- **Wakeonlan** packet installed *(optional since 0.1.2)*
67+
6468
- **Sreeen**, **NoHup** or **Tmux** packet installed
6569

6670
- Ability to **port-forward** and open up ports
@@ -270,6 +274,29 @@ It is compatible with theese hosts:
270274
- Solaris
271275

272276

277+
## Add WoL_API as startup service for Unix
278+
279+
Position yourself at the **init.d** folder:
280+
281+
cd /etc/init.d/
282+
283+
Paste the contents of the file in this repo named **wolapi_linux_service_template** into a file named **wolapi**.
284+
285+
Execute the following commands as **root**:
286+
287+
chmod +x wolapi
288+
chown root wolapi
289+
chgrp root wolapi
290+
update-rc.d wolapi defaults
291+
systemctl daemon-reload
292+
293+
You should now have a service called **wolapi** running that will start on boot.
294+
295+
273296
## Final Thoughts
274297

275298
> There are may be **many ways** to power on your devices when you are not at home, but we developed **our own solution** for this task that just works and **_provides a high security while having full control over it_**.
299+
300+
## Sources & Inspirations
301+
302+
Native Java WoL packets *(0.1.2)* based on: https://github.com/Cyecize/Remote-Wake-On-Lan-Web

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</parent>
1111
<groupId>com.em7</groupId>
1212
<artifactId>wol</artifactId>
13-
<version>0.1.1</version>
13+
<version>0.1.2</version>
1414
<packaging>jar</packaging>
1515
<name>wol</name>
1616
<description>Wake on Lan</description>
@@ -54,7 +54,7 @@
5454
</dependencies>
5555

5656
<build>
57-
<finalName>wol</finalName>
57+
<finalName>${project.artifactId}-${project.version}</finalName>
5858
<plugins>
5959
<plugin>
6060
<groupId>org.apache.maven.plugins</groupId>

src/main/java/com/em7/wol/controller/devices/DevicesController.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import com.em7.wol.dto.out.OutDeviceDTO;
55
import com.em7.wol.service.PingService;
6+
import com.em7.wol.service.WakeService;
67
import com.em7.wol.util.RestUtils;
78
import com.google.gson.Gson;
89
import com.google.gson.GsonBuilder;
@@ -38,6 +39,9 @@ public class DevicesController {
3839
@Autowired
3940
private PingService pingService;
4041

42+
@Autowired
43+
private WakeService wakeService;
44+
4145
@RequestMapping(value = "/getDevices", method = RequestMethod.GET)
4246
@ResponseBody
4347
public List<OutDeviceDTO> getDevices(HttpServletRequest request) {
@@ -93,6 +97,8 @@ public String sendWoL(Model model, HttpServletRequest request, @PathVariable Str
9397
model.addAttribute("username", username);
9498
model.addAttribute("devices", getDevices(request));
9599
log.info("Turning on device with ip: " + ip + " mac: " + mac);
100+
101+
// Method 1: Using wakeonlan via package installed client
96102
Process p;
97103
try {
98104
p = Runtime.getRuntime().exec("wakeonlan " + mac);
@@ -102,6 +108,10 @@ public String sendWoL(Model model, HttpServletRequest request, @PathVariable Str
102108
log.error("There was an error turning on device with ip: " + ip + " mac: " + mac);
103109
log.error("Error: " + e);
104110
}
111+
112+
// # Method 2: Using plain Java socket (I use both ports 7 and 9 for compatibility)
113+
wakeService.sendMagicPacket(ip, mac);
114+
105115
return "devices/list";
106116
}else{
107117
return "redirect:/";
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.em7.wol.service;
2+
3+
public interface WakeService {
4+
void sendMagicPacket(String ip, String mac);
5+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.em7.wol.service;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import lombok.extern.slf4j.Slf4j;
5+
import org.springframework.stereotype.Service;
6+
7+
import java.net.DatagramPacket;
8+
import java.net.DatagramSocket;
9+
import java.net.InetAddress;
10+
11+
import static com.em7.wol.service.WakeStrategy.BROADCAST_TO_IP;
12+
13+
@Slf4j
14+
@Service
15+
@RequiredArgsConstructor
16+
public class WakeServiceImpl implements WakeService {
17+
18+
private final WakeStrategy wakeStrategy = BROADCAST_TO_IP;
19+
20+
@Override
21+
public void sendMagicPacket(String ipStr, String macStr) {
22+
try {
23+
final byte[] macBytes = getMacBytes(macStr);
24+
final byte[] bytes = new byte[6 + 16 * macBytes.length];
25+
for (int i = 0; i < 6; i++) {
26+
bytes[i] = (byte) 0xff;
27+
}
28+
for (int i = 6; i < bytes.length; i += macBytes.length) {
29+
System.arraycopy(macBytes, 0, bytes, i, macBytes.length);
30+
}
31+
32+
final InetAddress address = InetAddress.getByName(this.wakeStrategy.processIp(ipStr));
33+
final DatagramPacket packet7 = new DatagramPacket(bytes, bytes.length, address, 7);
34+
final DatagramPacket packet9 = new DatagramPacket(bytes, bytes.length, address, 9);
35+
try (DatagramSocket socket = new DatagramSocket()) {
36+
socket.setBroadcast(this.wakeStrategy.isBroadcast());
37+
// Send to two main WoL ports
38+
socket.send(packet7);
39+
socket.send(packet9);
40+
}
41+
42+
log.info("Wake-on-LAN packet sent to ip: " + ipStr + " mac: " + macStr);
43+
} catch (Exception e) {
44+
log.error("Failed to send Wake-on-LAN packet to ip: " + ipStr + " mac: " + macStr + " -> ", e);
45+
}
46+
}
47+
48+
private static byte[] getMacBytes(String macStr) throws IllegalArgumentException {
49+
final byte[] bytes = new byte[6];
50+
final String[] hex = macStr.split("(\\:|\\-)");
51+
if (hex.length != 6) {
52+
throw new IllegalArgumentException("Invalid MAC address.");
53+
}
54+
try {
55+
for (int i = 0; i < 6; i++) {
56+
bytes[i] = (byte) Integer.parseInt(hex[i], 16);
57+
}
58+
} catch (NumberFormatException e) {
59+
throw new IllegalArgumentException("Invalid hex digit in MAC address.");
60+
}
61+
return bytes;
62+
}
63+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.em7.wol.service;
2+
3+
import lombok.RequiredArgsConstructor;
4+
5+
import java.util.function.Function;
6+
7+
@RequiredArgsConstructor
8+
public enum WakeStrategy {
9+
BROADCAST_TO_ALL(true, ip -> "255.255.255.255"),
10+
BROADCAST_TO_IP(true, ip -> ip.replaceAll("\\d{1,}$", "255")),
11+
SEND_TO_IP(false, ip -> ip);
12+
13+
private final boolean isBroadcast;
14+
private final Function<String, String> ipProcessor;
15+
16+
public String processIp(String ip) {
17+
return this.ipProcessor.apply(ip);
18+
}
19+
20+
public boolean isBroadcast() {
21+
return this.isBroadcast;
22+
}
23+
}

src/main/resources/application.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version=0.1.1
1+
version=0.1.2
22
spring.profiles.active=default
33

44
wol.user=admin

src/main/resources/devices.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
[
2+
{
3+
"description": "Raspberry Pi 5",
4+
"id": 1,
5+
"ip": "192.168.12.163",
6+
"mac": "00:16:96:FF:FF:FF",
7+
"name": "Dev Server",
8+
"status": false
9+
},
10+
{
11+
"description": "8700K",
12+
"id": 2,
13+
"ip": "192.168.12.228",
14+
"mac": "30:9C:23:FF:FF:FF",
15+
"name": "Desktop",
16+
"status": false
17+
},
18+
{
19+
"description": "ARM Linux Server",
20+
"id": 3,
21+
"ip": "192.168.12.140",
22+
"mac": "00:1E:06:FF:FF:FF",
23+
"name": "ODROID-XU4",
24+
"status": false
25+
},
26+
{
27+
"description": "No-Ip",
28+
"id": 4,
29+
"ip": "example.no-ip.biz",
30+
"mac": "10:1E:06:FF:FF:FF",
31+
"name": "London",
32+
"status": false
33+
},
34+
{
35+
"description": "Router",
36+
"id": 5,
37+
"ip": "192.168.1.1",
38+
"mac": "10:1E:06:FF:FF:FF",
39+
"name": "Gateway",
40+
"status": false
41+
},
42+
{
43+
"description": "Local device",
44+
"id": 6,
45+
"ip": "127.0.0.1",
46+
"mac": "F4:CF:A2:FF:FF:FF",
47+
"name": "This server",
48+
"status": false
49+
}
50+
]

wolapi_linux_service_template

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/bin/sh
2+
### BEGIN INIT INFO
3+
# Provides: wolapi
4+
# Required-Start: $local_fs $network
5+
# Required-Stop: $local_fs $network
6+
# Default-Start: 2 3 4 5
7+
# Default-Stop: 0 1 6
8+
# Description: Minimalist Wake-On-LAN API service
9+
### END INIT INFO
10+
11+
SCRIPT="java -jar /home/odroid/wol.jar"
12+
PIDFILE=/var/run/wolapi.pid
13+
14+
start() {
15+
echo "Starting wolapi..."
16+
nohup $SCRIPT >/dev/null 2>&1 &
17+
echo $! > $PIDFILE
18+
echo "wolapi started with PID $(cat $PIDFILE)"
19+
}
20+
21+
stop() {
22+
if [ -f $PIDFILE ]; then
23+
PID=$(cat $PIDFILE)
24+
echo "Stopping wolapi (PID $PID)..."
25+
kill $PID && rm -f $PIDFILE
26+
echo "wolapi stopped."
27+
else
28+
echo "wolapi is not running."
29+
fi
30+
}
31+
32+
case "$1" in
33+
start)
34+
start
35+
;;
36+
stop)
37+
stop
38+
;;
39+
restart)
40+
stop
41+
start
42+
;;
43+
*)
44+
echo "Usage: $0 {start|stop|restart}"
45+
exit 1
46+
;;
47+
esac

0 commit comments

Comments
 (0)