Skip to content

Commit a43519b

Browse files
committed
0.0.8 Add shutdown option for shutdown service running devices.
Signed-off-by: elmodo7 <elmodo7yt@gmail.com>
1 parent f4ab077 commit a43519b

File tree

8 files changed

+146
-9
lines changed

8 files changed

+146
-9
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
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.0.6</version>
13+
<version>0.0.8</version>
1414
<packaging>jar</packaging>
1515
<name>wol</name>
1616
<description>Wake on Lan</description>

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import java.io.InputStreamReader;
2121
import java.io.Reader;
2222
import java.lang.reflect.Type;
23+
import java.net.HttpURLConnection;
2324
import java.net.InetAddress;
25+
import java.net.URL;
2426
import java.util.ArrayList;
2527
import java.util.List;
2628

@@ -35,6 +37,9 @@ public class DevicesController {
3537
@Value("${version}")
3638
private String version;
3739

40+
@Value("${shutdown.port}")
41+
private Integer shutdownPort;
42+
3843
@RequestMapping(value = "/getDevices", method = RequestMethod.GET)
3944
@ResponseBody
4045
public List<OutDeviceDTO> getDevices(HttpServletRequest request) {
@@ -105,4 +110,31 @@ public String sendWoL(Model model, HttpServletRequest request, @PathVariable Str
105110
return "redirect:/";
106111
}
107112
}
113+
114+
@RequestMapping(path = "/devices/shutdown/{ip}", method = RequestMethod.GET)
115+
public String sendShutdown(Model model, HttpServletRequest request, @PathVariable String ip) {
116+
String username = (String) request.getSession().getAttribute("username");
117+
if(username != null && !username.isEmpty()) {
118+
model.addAttribute("username", username);
119+
model.addAttribute("devices", getDevices(request));
120+
log.info("Turning off device with ip: " + ip + " on shutdownPort: " + shutdownPort);
121+
try {
122+
URL url = new URL("http://" + ip + ":" + shutdownPort + "/shutdown");
123+
HttpURLConnection con = (HttpURLConnection) url.openConnection();
124+
con.setRequestMethod("GET");
125+
// Optional: Set timeouts for connection and read
126+
con.setConnectTimeout(5000);
127+
con.setReadTimeout(5000);
128+
// Check the response code
129+
int responseCode = con.getResponseCode();
130+
con.disconnect();
131+
} catch (Exception e) {
132+
log.error("There was an error turning off device with ip: " + ip + " on shutdownPort: " + shutdownPort);
133+
log.error("Error: " + e);
134+
}
135+
return "devices/list";
136+
}else{
137+
return "redirect:/";
138+
}
139+
}
108140
}

src/main/java/com/em7/wol/dto/out/OutDeviceDTO.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ public class OutDeviceDTO {
1010
private String ip;
1111
private String mac;
1212
private boolean status;
13+
private boolean shutdownable;
1314
}

src/main/java/com/em7/wol/service/PingService.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,20 @@
33
import com.em7.wol.dto.out.OutDeviceDTO;
44
import org.springframework.stereotype.Service;
55

6+
import java.io.IOException;
67
import java.net.InetAddress;
8+
import java.net.InetSocketAddress;
9+
import java.net.Socket;
10+
import java.util.ArrayList;
11+
import java.util.Arrays;
712
import java.util.List;
813
import java.util.concurrent.*;
914

1015
@Service
1116
public class PingService {
1217

18+
private final Integer shutdownPort = 7801;
19+
1320
public void pingDevicesConcurrently(List<OutDeviceDTO> outDeviceDTOs) {
1421
// Create a thread pool with a fixed number of threads (can be customized)
1522
ExecutorService executorService = Executors.newFixedThreadPool(10);
@@ -21,6 +28,7 @@ public void pingDevicesConcurrently(List<OutDeviceDTO> outDeviceDTOs) {
2128
try {
2229
InetAddress address = InetAddress.getByName(device.getIp());
2330
boolean reachable = address.isReachable(500); // 100ms timeout should be fine, normally we are on LAN but some devices like ESPs may take longer to respond
31+
device.setShutdownable(checkShutdownService(device.getIp()));
2432
device.setStatus(reachable);
2533
} catch (Exception e) {
2634
e.printStackTrace();
@@ -46,4 +54,31 @@ public void pingDevicesConcurrently(List<OutDeviceDTO> outDeviceDTOs) {
4654
}
4755
}
4856
}
57+
58+
public boolean checkShutdownService(String ip){
59+
ConcurrentLinkedQueue openPorts = new ConcurrentLinkedQueue<>();
60+
ExecutorService executorService = Executors.newFixedThreadPool(10);
61+
List<Integer> portsToScan = new ArrayList<>(Arrays.asList(shutdownPort));
62+
for(Integer port : portsToScan){
63+
executorService.submit(() -> {
64+
try {
65+
Socket socket = new Socket();
66+
socket.connect(new InetSocketAddress(ip, port), 500);
67+
socket.close();
68+
openPorts.add(port);
69+
} catch (IOException e) {}
70+
});
71+
}
72+
executorService.shutdown();
73+
try {
74+
executorService.awaitTermination(1, TimeUnit.MINUTES);
75+
} catch (InterruptedException e) {
76+
throw new RuntimeException(e);
77+
}
78+
List openPortList = new ArrayList<>();
79+
while (!openPorts.isEmpty()) {
80+
openPortList.add(openPorts.poll());
81+
}
82+
return openPortList.contains(shutdownPort);
83+
}
4984
}

src/main/resources/application.properties

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
version=0.0.6
1+
version=0.0.8
2+
spring.profiles.active=default
23

34
wol.user=admin
45
wol.password=admin
56
server.port=7800
7+
shutdown.port=7801
68

79
#security.require-ssl=false
810
#server.ssl.key-store-type=PKCS12

src/main/resources/devices.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
"status": false
99
},
1010
{
11-
"description": "8700K",
11+
"description": "google.com",
1212
"id": 2,
13-
"ip": "192.168.12.228",
13+
"ip": "8.8.8.8",
1414
"mac": "30:9C:23:FF:FF:FF",
15-
"name": "Desktop",
15+
"name": "Google",
1616
"status": false
1717
},
1818
{

src/main/resources/static/js/devices/list.js

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,19 @@ function getDevices() {
6666
data: null,
6767
render: function(data)
6868
{
69-
return '<div class="btn-group">'
70-
+ ' <button class="btn btn-default btn-wol" name="' + data.name + '" mac="' + data.mac + '" + ip="' + data.ip + '" title="Send WoL"><i class="fas fa-power-off"></i></button>'
71-
+ ' </div>';
69+
var btnHtml = '';
70+
var wolVisibleClass = "d-none";
71+
var shutdownVisibleClass = "d-none";
72+
if(data.shutdownable && data.status){
73+
shutdownVisibleClass = "";
74+
}else if(!data.status){
75+
wolVisibleClass = "";
76+
}
77+
78+
btnHtml += '<div id="btn-wol-' + data.id + '" class="btn-group ' + wolVisibleClass + '"><button class="btn btn-default btn-wol bg-success" name="' + data.name + '" mac="' + data.mac + '" + ip="' + data.ip + '" title="Send WoL"><i class="fas fa-power-off"></i></button></div>';
79+
btnHtml += '<div id="btn-shutdown-' + data.id + '" class="btn-group ' + shutdownVisibleClass + '"><button class="btn btn-default btn-shutdown bg-danger" name="' + data.name + '" mac="' + data.mac + '" + ip="' + data.ip + '" title="Shutdown"><i class="fas fa-power-off"></i></button></div>';
80+
81+
return btnHtml;
7282
},
7383
className: 'text-center',
7484
searchable: false,
@@ -97,6 +107,28 @@ function getDevices() {
97107
}
98108
});
99109
});
110+
111+
$(".btn-shutdown").unbind("click");
112+
$(".btn-shutdown").click(function(){
113+
var name = $(this).attr("name");
114+
var mac = $(this).attr("mac");
115+
var ip = $(this).attr("ip");
116+
wolModal = $.confirm({
117+
title: 'Shutdown Confirmation',
118+
content: 'Do you want to shut <b>' + name + '</b> down?',
119+
type: 'red',
120+
typeAnimated: true,
121+
buttons: {
122+
"Shutdown!": function () {
123+
shutdown(name, mac, ip);
124+
wolModal.close();
125+
},
126+
cancel: function () {
127+
wolModal.close();
128+
}
129+
}
130+
});
131+
});
100132
},
101133
});
102134
}
@@ -108,6 +140,13 @@ function sendWoL(name, mac, ip) {
108140
xhttp.send();
109141
}
110142

143+
function shutdown(name, mac, ip) {
144+
notifyShutdown(name);
145+
var xhttp = new XMLHttpRequest();
146+
xhttp.open("GET", "./shutdown/" + ip, true);
147+
xhttp.send();
148+
}
149+
111150
function notifyWoL(name){
112151
toastr.options = {
113152
"closeButton": false,
@@ -129,16 +168,43 @@ function notifyWoL(name){
129168
toastr["success"]("Turning " + name + " on.", "WoL Sent")
130169
}
131170

171+
function notifyShutdown(name){
172+
toastr.options = {
173+
"closeButton": false,
174+
"debug": false,
175+
"newestOnTop": false,
176+
"progressBar": false,
177+
"positionClass": "toast-top-center",
178+
"preventDuplicates": false,
179+
"onclick": null,
180+
"showDuration": "300",
181+
"hideDuration": "1000",
182+
"timeOut": "5000",
183+
"extendedTimeOut": "1000",
184+
"showEasing": "swing",
185+
"hideEasing": "linear",
186+
"showMethod": "fadeIn",
187+
"hideMethod": "fadeOut"
188+
}
189+
toastr["error"]("Turning " + name + " off.", "Shutdown Sent")
190+
}
191+
132192
function threadGetDevices(){
133193
window.setInterval(function () {
134194
httpGetAsync("/getDevices", function (devices) {
135195
var devices = JSON.parse(devices);
136196
if(devices != null && devices.length > 0){
137197
devices.forEach(function callback(device, index, array) {
198+
$("#btn-wol-" + device.id).addClass("d-none");
199+
$("#btn-shutdown-" + device.id).addClass("d-none");
138200
if(device.status){
139201
$("#img" + device.id).attr("src", "/img/online.png");
140-
}else{
202+
if(device.shutdownable){
203+
$("#btn-shutdown-" + device.id).removeClass("d-none");
204+
}
205+
}else if(!device.status){
141206
$("#img" + device.id).attr("src", "/img/offline.png");
207+
$("#btn-wol-" + device.id).removeClass("d-none");
142208
}
143209
});
144210
}

src/main/resources/templates/devices/list.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<head>
44
<meta charset="utf-8"/>
55
<meta name="viewport" content="width=device-width, initial-scale=1"/>
6+
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
67
<title>WoL | Device List</title>
78
<link rel="stylesheet" th:href="@{/css/dataTables.bootstrap4.min.css}" type="text/css" charset="utf-8"/>
89
<link rel="stylesheet" th:href="@{/css/responsive.bootstrap4.min.css}" type="text/css"/>

0 commit comments

Comments
 (0)