2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.souliss.internal.handler;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.concurrent.ExecutorService;
18 import java.util.concurrent.Executors;
19 import java.util.concurrent.Future;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.souliss.internal.SoulissBindingConstants;
26 import org.openhab.binding.souliss.internal.config.GatewayConfig;
27 import org.openhab.binding.souliss.internal.discovery.DiscoverResult;
28 import org.openhab.binding.souliss.internal.discovery.SoulissGatewayDiscovery;
29 import org.openhab.binding.souliss.internal.protocol.CommonCommands;
30 import org.openhab.binding.souliss.internal.protocol.SendDispatcherRunnable;
31 import org.openhab.binding.souliss.internal.protocol.UDPListenDiscoverRunnable;
32 import org.openhab.core.common.NamedThreadFactory;
33 import org.openhab.core.thing.Bridge;
34 import org.openhab.core.thing.ChannelUID;
35 import org.openhab.core.thing.Thing;
36 import org.openhab.core.thing.ThingStatus;
37 import org.openhab.core.thing.ThingStatusDetail;
38 import org.openhab.core.thing.binding.BaseBridgeHandler;
39 import org.openhab.core.thing.binding.ThingHandlerService;
40 import org.openhab.core.types.Command;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
45 * The {@link SoulissGatewayHandler} is responsible for handling commands, which are
46 * sent to one of the channels.
48 * @author Tonino Fazio - Initial contribution
49 * @author Luca Calcaterra - Refactor for OH3
52 public class SoulissGatewayHandler extends BaseBridgeHandler {
54 private final Logger logger = LoggerFactory.getLogger(SoulissGatewayHandler.class);
56 private final CommonCommands commonCommands = new CommonCommands();
58 private @Nullable ExecutorService udpExecutorService;
60 private @Nullable Future<?> udpListenerJob;
61 private @Nullable ScheduledFuture<?> pingScheduler;
62 private @Nullable ScheduledFuture<?> subscriptionScheduler;
63 private @Nullable ScheduledFuture<?> healthScheduler;
65 boolean bGatewayDetected = false;
67 private @Nullable SoulissGatewayDiscovery discoveryService;
69 public @Nullable DiscoverResult discoverResult = null;
71 public boolean thereIsAThingDetection = true;
73 private Bridge bridge;
75 private int nodes = 0;
76 private int maxTypicalXnode = 24;
77 private int countPingKo = 0;
79 private GatewayConfig gwConfig = new GatewayConfig();
81 public GatewayConfig getGwConfig() {
85 public SoulissGatewayHandler(Bridge br) {
91 public void handleCommand(ChannelUID channelUID, Command command) {
96 public Collection<Class<? extends ThingHandlerService>> getServices() {
97 return Collections.singleton(SoulissGatewayDiscovery.class);
101 public void initialize() {
102 gwConfig = getConfigAs(GatewayConfig.class);
104 logger.debug("Starting UDP server on Souliss Default Port for Topics (Publish&Subcribe)");
106 // new runnable udp listener
107 var udpServerDefaultPortRunnableClass = new UDPListenDiscoverRunnable(this.bridge, this.discoverResult);
108 // and exec on thread
109 var localUdpListenerJob = this.udpListenerJob;
110 if (localUdpListenerJob == null || localUdpListenerJob.isCancelled()) {
111 var localUdpExecutorService = this.udpExecutorService;
112 localUdpExecutorService = Executors
113 .newSingleThreadExecutor(new NamedThreadFactory(getThing().getUID().getAsString()));
114 localUdpExecutorService.submit(udpServerDefaultPortRunnableClass);
118 var soulissGatewayJobPingRunnable = new SoulissGatewayJobPing(this.bridge);
119 pingScheduler = scheduler.scheduleWithFixedDelay(soulissGatewayJobPingRunnable, 2, this.gwConfig.pingInterval,
122 var soulissGatewayJobSubscriptionRunnable = new SoulissGatewayJobSubscription(bridge);
123 subscriptionScheduler = scheduler.scheduleWithFixedDelay(soulissGatewayJobSubscriptionRunnable, 5,
124 this.gwConfig.subscriptionInterval, TimeUnit.SECONDS);
126 // JOB HEALTH OF NODES
127 var soulissGatewayJobHealthyRunnable = new SoulissGatewayJobHealthy(this.bridge);
128 healthScheduler = scheduler.scheduleWithFixedDelay(soulissGatewayJobHealthyRunnable, 5,
129 this.gwConfig.healthyInterval, TimeUnit.SECONDS);
131 var soulissSendDispatcherRunnable = new SendDispatcherRunnable(this.bridge);
132 scheduler.scheduleWithFixedDelay(soulissSendDispatcherRunnable, 15,
133 SoulissBindingConstants.SEND_DISPATCHER_MIN_DELAY_CYCLE_IN_MILLIS, TimeUnit.MILLISECONDS);
136 public void dbStructAnswerReceived() {
137 commonCommands.sendTypicalRequestFrame(this.gwConfig, nodes);
140 public void setNodes(int nodes) {
144 public int getNodes() {
146 for (Thing thing : getThing().getThings()) {
147 if (thing.getThingTypeUID().equals(SoulissBindingConstants.TOPICS_THING_TYPE)) {
150 var cfg = thing.getConfiguration();
151 var props = cfg.getProperties();
152 var pNode = props.get("node");
154 var thingNode = Integer.parseInt(pNode.toString());
156 if (thingNode > maxNode) {
160 // at the end the length of the list will be equal to the number of present nodes
165 public void setMaxTypicalXnode(int maxTypicalXnode) {
166 this.maxTypicalXnode = maxTypicalXnode;
169 public int getMaxTypicalXnode() {
170 return maxTypicalXnode;
174 * The {@link gatewayDetected} is used to notify that UDPServer decoded a Ping Response from gateway
177 public void gatewayDetected() {
178 updateStatus(ThingStatus.ONLINE);
183 public void pingSent() {
184 if (++countPingKo > 3) {
185 var bridgeHandler = bridge.getHandler();
186 if (bridgeHandler != null) {
187 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
188 "Gateway " + bridgeHandler.getThing().getUID() + " do not respond to " + countPingKo + " ping");
193 public void sendSubscription() {
194 if (this.gwConfig.gatewayLanAddress.length() > 0) {
195 int totNodes = getNodes();
196 commonCommands.sendSUBSCRIPTIONframe(this.gwConfig, totNodes);
198 logger.debug("Sent subscription packet");
201 public void setThereIsAThingDetection() {
202 thereIsAThingDetection = true;
205 public void resetThereIsAThingDetection() {
206 thereIsAThingDetection = false;
209 public @Nullable SoulissGatewayDiscovery getDiscoveryService() {
210 return this.discoveryService;
213 public void setDiscoveryService(SoulissGatewayDiscovery discoveryService) {
214 this.discoveryService = discoveryService;
218 public void dispose() {
219 var localPingScheduler = this.pingScheduler;
220 if (localPingScheduler != null) {
221 localPingScheduler.cancel(true);
223 var localSubscriptionScheduler = this.subscriptionScheduler;
224 if (localSubscriptionScheduler != null) {
225 localSubscriptionScheduler.cancel(true);
227 var localHealthScheduler = this.healthScheduler;
228 if (localHealthScheduler != null) {
229 localHealthScheduler.cancel(true);
231 var localUdpListenerJob = this.udpListenerJob;
232 if (localUdpListenerJob != null) {
233 localUdpListenerJob.cancel(true);
235 var localUdpExecutorService = this.udpExecutorService;
236 if (localUdpExecutorService != null) {
237 localUdpExecutorService.shutdownNow();
243 public void setBridgeStatus(boolean isOnline) {
244 logger.debug("setBridgeStatus(): Setting Bridge to {}", isOnline ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
246 updateStatus(isOnline ? ThingStatus.ONLINE : ThingStatus.OFFLINE);