]> git.basschouten.com Git - openhab-addons.git/blob
7baa573bf9b2174db8634a1c2b1b1bd66fe04e6b
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.samsungtv.internal;
14
15 import static org.openhab.binding.samsungtv.internal.SamsungTvBindingConstants.*;
16
17 import java.util.Optional;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.openhab.binding.samsungtv.internal.handler.SamsungTvHandler;
23 import org.openhab.binding.samsungtv.internal.service.RemoteControllerService;
24 import org.openhab.binding.samsungtv.internal.service.api.SamsungTvService;
25 import org.openhab.core.library.types.OnOffType;
26 import org.openhab.core.types.Command;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * The {@link WolSend} is responsible for sending WOL packets and resending of commands
32  * Used by {@link SamsungTvHandler}
33  *
34  * @author Nick Waterton - Initial contribution
35  */
36 @NonNullByDefault
37 public class WolSend {
38
39     private static final int WOL_PACKET_RETRY_COUNT = 10;
40     private static final int WOL_SERVICE_CHECK_COUNT = 30;
41
42     private final Logger logger = LoggerFactory.getLogger(WolSend.class);
43
44     private String host = "";
45
46     int wolCount = 0;
47     String channel = POWER;
48     Command command = OnOffType.ON;
49     String macAddress = "";
50     private Optional<ScheduledFuture<?>> wolJob = Optional.empty();
51     SamsungTvHandler handler;
52
53     public WolSend(SamsungTvHandler handler) {
54         this.handler = handler;
55     }
56
57     /**
58      * Send multiple WOL packets spaced with 100ms intervals and resend command
59      *
60      * @param channel Channel to resend command on
61      * @param command Command to resend
62      * @return boolean true/false if WOL job started
63      */
64     public boolean send(String channel, Command command) {
65         this.host = handler.host;
66         if (channel.equals(POWER) || channel.equals(ART_MODE)) {
67             if (OnOffType.ON.equals(command)) {
68                 macAddress = handler.configuration.getMacAddress();
69                 if (macAddress.isBlank()) {
70                     logger.debug("{}: Cannot send WOL packet, MAC address invalid: {}", host, macAddress);
71                     return false;
72                 }
73                 this.channel = channel;
74                 this.command = command;
75                 if (channel.equals(ART_MODE) && !handler.getArtModeSupported()) {
76                     logger.debug("{}: artMode is not yet detected on this TV - sending WOL anyway", host);
77                 }
78                 startWoljob();
79                 return true;
80             } else {
81                 cancel();
82             }
83         }
84         return false;
85     }
86
87     private void startWoljob() {
88         wolJob.ifPresentOrElse(job -> {
89             if (job.isCancelled()) {
90                 start();
91             } else {
92                 logger.debug("{}: WOL job already running", host);
93             }
94         }, () -> {
95             start();
96         });
97     }
98
99     public void start() {
100         wolCount = 0;
101         wolJob = Optional.of(
102                 handler.getScheduler().scheduleWithFixedDelay(this::wolCheckPeriodic, 0, 1000, TimeUnit.MILLISECONDS));
103     }
104
105     public synchronized void cancel() {
106         wolJob.ifPresent(job -> {
107             logger.debug("{}: cancelling WOL Job", host);
108             job.cancel(true);
109         });
110     }
111
112     private void sendWOL() {
113         logger.debug("{}: Send WOL packet to {}", host, macAddress);
114
115         // send max 10 WOL packets with 100ms intervals
116         for (int i = 0; i < WOL_PACKET_RETRY_COUNT; i++) {
117             handler.getScheduler().schedule(() -> {
118                 WakeOnLanUtility.sendWOLPacket(macAddress);
119             }, (i * 100), TimeUnit.MILLISECONDS);
120         }
121     }
122
123     private void sendCommand(RemoteControllerService service) {
124         // send command in 2 seconds to allow time for connection to re-establish
125         logger.debug("{}: resend command {} to channel {} in 2 seconds...", host, command, channel);
126         handler.getScheduler().schedule(() -> {
127             service.handleCommand(channel, command);
128         }, 2000, TimeUnit.MILLISECONDS);
129     }
130
131     private void wolCheckPeriodic() {
132         if (wolCount % 10 == 0) {
133             // resend WOL every 10 seconds
134             sendWOL();
135         }
136         // after RemoteService up again to ensure state is properly set
137         Optional<SamsungTvService> service = handler.findServiceInstance(RemoteControllerService.SERVICE_NAME);
138         service.ifPresent(s -> {
139             logger.debug("{}: RemoteControllerService found after {} attempts", host, wolCount);
140             // do not resend command if artMode command as TV wakes up in artMode
141             if (!channel.equals(ART_MODE)) {
142                 sendCommand((RemoteControllerService) s);
143             }
144             cancel();
145         });
146         // cancel job
147         if (wolCount++ > WOL_SERVICE_CHECK_COUNT) {
148             logger.warn("{}: Service NOT found after {} attempts: stopping WOL attempts", host, wolCount);
149             cancel();
150             handler.putOffline();
151         }
152     }
153 }