]> git.basschouten.com Git - openhab-addons.git/blob
08468fac5302d0d04f21bdc7887dd43909ec18d1
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.milight.internal.handler;
14
15 import java.io.IOException;
16 import java.net.InetAddress;
17 import java.time.Instant;
18 import java.time.LocalDateTime;
19 import java.time.ZoneId;
20 import java.time.format.DateTimeFormatter;
21 import java.time.format.FormatStyle;
22 import java.util.concurrent.ScheduledFuture;
23 import java.util.concurrent.TimeUnit;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.binding.milight.internal.MilightBindingConstants;
28 import org.openhab.binding.milight.internal.protocol.MilightV6SessionManager;
29 import org.openhab.binding.milight.internal.protocol.MilightV6SessionManager.ISessionState;
30 import org.openhab.binding.milight.internal.protocol.MilightV6SessionManager.SessionState;
31 import org.openhab.core.config.core.Configuration;
32 import org.openhab.core.thing.Bridge;
33 import org.openhab.core.thing.ThingStatus;
34 import org.openhab.core.thing.ThingStatusDetail;
35
36 /**
37  * The {@link BridgeV6Handler} is responsible for handling commands, which are
38  * sent to one of the channels.
39  *
40  * @author David Graeff - Initial contribution
41  */
42 @NonNullByDefault
43 public class BridgeV6Handler extends AbstractBridgeHandler implements ISessionState {
44     private @NonNullByDefault({}) MilightV6SessionManager session;
45     final DateTimeFormatter timeFormat = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);
46     private String offlineReason = "";
47     private @Nullable ScheduledFuture<?> scheduledFuture;
48
49     public BridgeV6Handler(Bridge bridge, int bridgeOffset) {
50         super(bridge, bridgeOffset);
51     }
52
53     /**
54      * Creates a session manager object and the send queue. The initial IP address may be null
55      * or is not matching with the real IP address of the bridge. The session manager will send
56      * a broadcast packet to find the bridge with the respective bridge ID and will change the
57      * IP address of the send queue object accordingly.
58      *
59      * The keep alive timer that is also setup here, will send keep alive packets periodically.
60      * If the bridge doesn't respond anymore (e.g. DHCP IP change), the initial session handshake
61      * starts all over again.
62      */
63     @Override
64     protected void startConnectAndKeepAlive() {
65         if (!config.bridgeid.matches("^([0-9A-Fa-f]{12})$")) {
66             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "bridgeID invalid!");
67             return;
68         }
69
70         if (config.port == 0) {
71             config.port = MilightBindingConstants.PORT_VER6;
72         }
73
74         updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "Waiting for session");
75         int refreshTime = Math.max(Math.min(config.refreshTime, MilightV6SessionManager.TIMEOUT_MS), 100);
76         this.session = new MilightV6SessionManager(config.bridgeid, this, address, config.port, refreshTime,
77                 new byte[] { (byte) config.passwordByte1, (byte) config.passwordByte2 });
78         session.start().thenAccept(d -> this.socket = d);
79     }
80
81     @Override
82     public void dispose() {
83         stopMakeOfflineTimer();
84         try {
85             session.close();
86         } catch (IOException ignore) {
87         }
88         this.session = null;
89         super.dispose();
90     }
91
92     public MilightV6SessionManager getSessionManager() {
93         return session;
94     }
95
96     @Override
97     public void sessionStateChanged(SessionState state, @Nullable InetAddress newAddress) {
98         stopMakeOfflineTimer();
99         switch (state) {
100             case SESSION_VALID_KEEP_ALIVE:
101                 preventReinit = true;
102                 Instant lastSessionTime = session.getLastSessionValidConfirmation();
103                 LocalDateTime date = LocalDateTime.ofInstant(lastSessionTime, ZoneId.systemDefault());
104                 updateProperty(MilightBindingConstants.PROPERTY_SESSIONCONFIRMED, date.format(timeFormat));
105                 preventReinit = false;
106                 break;
107             case SESSION_VALID:
108                 if (newAddress == null) {
109                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No IP address received");
110                     break;
111                 }
112                 if (!newAddress.equals(address) || !thing.getStatus().equals(ThingStatus.ONLINE)) {
113                     updateStatus(ThingStatus.ONLINE);
114                     this.address = newAddress;
115                     // As soon as the session is valid, update the user visible configuration of the host IP.
116                     Configuration c = editConfiguration();
117                     c.put(BridgeHandlerConfig.CONFIG_HOST_NAME, newAddress.getHostAddress());
118                     thing.setProperty(MilightBindingConstants.PROPERTY_SESSIONID, session.getSession());
119                     thing.setProperty(MilightBindingConstants.PROPERTY_SESSIONCONFIRMED,
120                             String.valueOf(session.getLastSessionValidConfirmation()));
121                     preventReinit = true;
122                     updateConfiguration(c);
123                     preventReinit = false;
124                 }
125                 break;
126             case SESSION_INVALID:
127                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
128                         "Session could not be established");
129
130                 break;
131             default:
132                 // Delay putting the session offline
133                 offlineReason = state.name();
134                 scheduledFuture = scheduler.schedule(
135                         () -> updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, offlineReason),
136                         1000, TimeUnit.MILLISECONDS);
137                 break;
138         }
139     }
140
141     private void stopMakeOfflineTimer() {
142         ScheduledFuture<?> future = scheduledFuture;
143         if (future != null) {
144             future.cancel(false);
145             scheduledFuture = null;
146         }
147     }
148 }