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.omnilink.internal.handler;
15 import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
17 import java.io.IOException;
18 import java.net.UnknownHostException;
19 import java.time.ZonedDateTime;
20 import java.util.Collection;
22 import java.util.Optional;
24 import java.util.concurrent.ScheduledFuture;
25 import java.util.concurrent.TimeUnit;
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.openhab.binding.omnilink.internal.AudioPlayer;
30 import org.openhab.binding.omnilink.internal.SystemType;
31 import org.openhab.binding.omnilink.internal.TemperatureFormat;
32 import org.openhab.binding.omnilink.internal.action.OmnilinkActions;
33 import org.openhab.binding.omnilink.internal.config.OmnilinkBridgeConfig;
34 import org.openhab.binding.omnilink.internal.discovery.OmnilinkDiscoveryService;
35 import org.openhab.binding.omnilink.internal.exceptions.BridgeOfflineException;
36 import org.openhab.core.library.types.DateTimeType;
37 import org.openhab.core.library.types.DecimalType;
38 import org.openhab.core.library.types.StringType;
39 import org.openhab.core.thing.Bridge;
40 import org.openhab.core.thing.ChannelUID;
41 import org.openhab.core.thing.Thing;
42 import org.openhab.core.thing.ThingStatus;
43 import org.openhab.core.thing.ThingStatusDetail;
44 import org.openhab.core.thing.ThingTypeUID;
45 import org.openhab.core.thing.binding.BaseBridgeHandler;
46 import org.openhab.core.thing.binding.ThingHandlerService;
47 import org.openhab.core.types.Command;
48 import org.openhab.core.types.RefreshType;
49 import org.openhab.core.types.UnDefType;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
53 import com.digitaldan.jomnilinkII.Connection;
54 import com.digitaldan.jomnilinkII.DisconnectListener;
55 import com.digitaldan.jomnilinkII.Message;
56 import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage;
57 import com.digitaldan.jomnilinkII.MessageTypes.EventLogData;
58 import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
59 import com.digitaldan.jomnilinkII.MessageTypes.SecurityCodeValidation;
60 import com.digitaldan.jomnilinkII.MessageTypes.SystemFeatures;
61 import com.digitaldan.jomnilinkII.MessageTypes.SystemFormats;
62 import com.digitaldan.jomnilinkII.MessageTypes.SystemInformation;
63 import com.digitaldan.jomnilinkII.MessageTypes.SystemStatus;
64 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAccessControlReaderLockStatus;
65 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAreaStatus;
66 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAudioZoneStatus;
67 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAuxSensorStatus;
68 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedThermostatStatus;
69 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedUnitStatus;
70 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedZoneStatus;
71 import com.digitaldan.jomnilinkII.MessageTypes.statuses.Status;
72 import com.digitaldan.jomnilinkII.MessageTypes.systemevents.AllOnOffEvent;
73 import com.digitaldan.jomnilinkII.MessageTypes.systemevents.ButtonEvent;
74 import com.digitaldan.jomnilinkII.MessageTypes.systemevents.SwitchPressEvent;
75 import com.digitaldan.jomnilinkII.MessageTypes.systemevents.SystemEvent;
76 import com.digitaldan.jomnilinkII.MessageTypes.systemevents.UPBLinkEvent;
77 import com.digitaldan.jomnilinkII.NotificationListener;
78 import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
79 import com.digitaldan.jomnilinkII.OmniNotConnectedException;
80 import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
81 import com.google.gson.Gson;
84 * The {@link OmnilinkBridgeHandler} defines some methods that are used to
85 * interface with an OmniLink Controller. This by extension also defines the
86 * OmniLink bridge that openHAB will be able to pick up and interface with.
88 * @author Craig Hamilton - Initial contribution
89 * @author Ethan Dye - openHAB3 rewrite
92 public class OmnilinkBridgeHandler extends BaseBridgeHandler implements NotificationListener, DisconnectListener {
93 private final Logger logger = LoggerFactory.getLogger(OmnilinkBridgeHandler.class);
94 private @Nullable Connection omniConnection = null;
95 private @Nullable ScheduledFuture<?> connectJob;
96 private @Nullable ScheduledFuture<?> eventPollingJob;
97 private final int autoReconnectPeriod = 60;
98 private Optional<AudioPlayer> audioPlayer = Optional.empty();
99 private Optional<SystemType> systemType = Optional.empty();
100 private final Gson gson = new Gson();
101 private int eventLogNumber = 0;
103 public OmnilinkBridgeHandler(Bridge bridge) {
108 public Collection<Class<? extends ThingHandlerService>> getServices() {
109 return Set.of(OmnilinkDiscoveryService.class, OmnilinkActions.class);
112 public void sendOmnilinkCommand(final int message, final int param1, final int param2)
113 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
115 getOmniConnection().controllerCommand(message, param1, param2);
116 } catch (IOException | OmniNotConnectedException e) {
117 setOfflineAndReconnect(e.getMessage());
118 throw new BridgeOfflineException(e);
122 public SecurityCodeValidation reqSecurityCodeValidation(int area, int digit1, int digit2, int digit3, int digit4)
123 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
125 return getOmniConnection().reqSecurityCodeValidation(area, digit1, digit2, digit3, digit4);
126 } catch (IOException | OmniNotConnectedException e) {
127 setOfflineAndReconnect(e.getMessage());
128 throw new BridgeOfflineException(e);
132 public void activateKeypadEmergency(int area, int emergencyType)
133 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
135 getOmniConnection().activateKeypadEmergency(area, emergencyType);
136 } catch (IOException | OmniNotConnectedException e) {
137 setOfflineAndReconnect(e.getMessage());
138 throw new BridgeOfflineException(e);
142 public SystemInformation reqSystemInformation()
143 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
145 return getOmniConnection().reqSystemInformation();
146 } catch (IOException | OmniNotConnectedException e) {
147 setOfflineAndReconnect(e.getMessage());
148 throw new BridgeOfflineException(e);
152 public SystemFormats reqSystemFormats()
153 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
155 return getOmniConnection().reqSystemFormats();
156 } catch (IOException | OmniNotConnectedException e) {
157 setOfflineAndReconnect(e.getMessage());
158 throw new BridgeOfflineException(e);
162 public void synchronizeControllerTime(ZonedDateTime zdt) {
163 boolean inDaylightSavings = zdt.getZone().getRules().isDaylightSavings(zdt.toInstant());
165 getOmniConnection().setTimeCommand(zdt.getYear() - 2000, zdt.getMonthValue(), zdt.getDayOfMonth(),
166 zdt.getDayOfWeek().getValue(), zdt.getHour(), zdt.getMinute(), inDaylightSavings);
167 } catch (IOException | OmniNotConnectedException | OmniInvalidResponseException
168 | OmniUnknownMessageTypeException e) {
169 logger.debug("Could not send set date time command to OmniLink Controller: {}", e.getMessage());
173 private SystemFeatures reqSystemFeatures()
174 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
176 return getOmniConnection().reqSystemFeatures();
177 } catch (IOException | OmniNotConnectedException e) {
178 setOfflineAndReconnect(e.getMessage());
179 throw new BridgeOfflineException(e);
184 public void handleCommand(ChannelUID channelUID, Command command) {
185 logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
187 if (command instanceof RefreshType) {
192 switch (channelUID.getId()) {
193 case CHANNEL_CONSOLE_ENABLE_DISABLE_BEEPER:
194 if (command instanceof StringType stringCommand) {
196 sendOmnilinkCommand(CommandMessage.CMD_CONSOLE_ENABLE_DISABLE_BEEPER,
197 stringCommand.equals(StringType.valueOf("OFF")) ? 0 : 1, 0);
198 updateState(CHANNEL_CONSOLE_ENABLE_DISABLE_BEEPER, UnDefType.UNDEF);
199 } catch (NumberFormatException | OmniInvalidResponseException | OmniUnknownMessageTypeException
200 | BridgeOfflineException e) {
201 logger.debug("Could not send Console command to OmniLink Controller: {}", e.getMessage());
204 logger.debug("Invalid command: {}, must be StringType", command);
207 case CHANNEL_CONSOLE_BEEP:
208 if (command instanceof DecimalType decimalCommand) {
210 sendOmnilinkCommand(CommandMessage.CMD_CONSOLE_BEEP, decimalCommand.intValue(), 0);
211 updateState(CHANNEL_CONSOLE_BEEP, UnDefType.UNDEF);
212 } catch (NumberFormatException | OmniInvalidResponseException | OmniUnknownMessageTypeException
213 | BridgeOfflineException e) {
214 logger.debug("Could not send Console command to OmniLink Controller: {}", e.getMessage());
217 logger.debug("Invalid command: {}, must be DecimalType", command);
221 logger.warn("Unknown channel for Bridge thing: {}", channelUID);
225 private void makeOmnilinkConnection() {
226 final Connection connection = omniConnection;
227 if (connection != null && connection.connected()) {
231 logger.debug("Attempting to connect to controller!");
233 OmnilinkBridgeConfig config = getConfigAs(OmnilinkBridgeConfig.class);
235 this.omniConnection = new Connection(config.getIpAddress(), config.getPort(),
236 config.getKey1() + ":" + config.getKey2());
239 * HAI only supports one audio player - cycle through features until we find a feature that is an audio
242 audioPlayer = reqSystemFeatures().getFeatures().stream()
243 .map(featureCode -> AudioPlayer.getAudioPlayerForFeatureCode(featureCode))
244 .filter(Optional::isPresent).findFirst().orElse(Optional.empty());
246 systemType = SystemType.getType(reqSystemInformation().getModel());
248 if (config.getLogPollingInterval() > 0) {
249 startEventPolling(config.getLogPollingInterval());
252 final Connection connectionNew = omniConnection;
253 if (connectionNew != null) {
254 connectionNew.enableNotifications();
255 connectionNew.addNotificationListener(OmnilinkBridgeHandler.this);
256 connectionNew.addDisconnectListener(this);
259 updateStatus(ThingStatus.ONLINE);
260 cancelReconnectJob(false);
262 updateBridgeProperties();
263 } catch (UnknownHostException e) {
264 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
265 } catch (IOException e) {
266 final Throwable cause = e.getCause();
268 final String causeMessage = cause.getMessage();
270 if (causeMessage != null && causeMessage.contains("Connection timed out")) {
271 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
272 "IP Address probably incorrect, timed out creating connection!");
274 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, causeMessage);
277 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
279 } catch (Exception e) {
280 setOfflineAndReconnect(e.getMessage());
281 logger.debug("Error connecting to OmniLink Controller: {}", e.getMessage());
286 public void objectStatusNotification(@Nullable ObjectStatus objectStatus) {
287 if (objectStatus != null) {
288 Status[] statuses = objectStatus.getStatuses();
289 for (Status status : statuses) {
290 if (status instanceof ExtendedUnitStatus unitStatus) {
291 int unitNumber = unitStatus.getNumber();
293 logger.debug("Received status update for Unit: {}, status: {}", unitNumber, unitStatus);
294 Optional<Thing> theThing = getUnitThing(unitNumber);
295 theThing.map(Thing::getHandler)
296 .ifPresent(theHandler -> ((UnitHandler) theHandler).handleStatus(unitStatus));
297 } else if (status instanceof ExtendedZoneStatus zoneStatus) {
298 int zoneNumber = zoneStatus.getNumber();
300 logger.debug("Received status update for Zone: {}, status: {}", zoneNumber, zoneStatus);
301 Optional<Thing> theThing = getChildThing(THING_TYPE_ZONE, zoneNumber);
302 theThing.map(Thing::getHandler)
303 .ifPresent(theHandler -> ((ZoneHandler) theHandler).handleStatus(zoneStatus));
304 } else if (status instanceof ExtendedAreaStatus areaStatus) {
305 int areaNumber = areaStatus.getNumber();
307 logger.debug("Received status update for Area: {}, status: {}", areaNumber, areaStatus);
308 systemType.ifPresent(t -> {
309 Optional<Thing> theThing = Optional.empty();
312 theThing = getChildThing(THING_TYPE_LUMINA_AREA, areaNumber);
315 theThing = getChildThing(THING_TYPE_OMNI_AREA, areaNumber);
318 theThing.map(Thing::getHandler)
319 .ifPresent(theHandler -> ((AbstractAreaHandler) theHandler).handleStatus(areaStatus));
321 } else if (status instanceof ExtendedAccessControlReaderLockStatus lockStatus) {
322 int lockNumber = lockStatus.getNumber();
324 logger.debug("Received status update for Lock: {}, status: {}", lockNumber, lockStatus);
325 Optional<Thing> theThing = getChildThing(THING_TYPE_LOCK, lockNumber);
326 theThing.map(Thing::getHandler)
327 .ifPresent(theHandler -> ((LockHandler) theHandler).handleStatus(lockStatus));
328 } else if (status instanceof ExtendedThermostatStatus thermostatStatus) {
329 int thermostatNumber = thermostatStatus.getNumber();
331 logger.debug("Received status update for Thermostat: {}, status: {}", thermostatNumber,
333 Optional<Thing> theThing = getChildThing(THING_TYPE_THERMOSTAT, thermostatNumber);
334 theThing.map(Thing::getHandler)
335 .ifPresent(theHandler -> ((ThermostatHandler) theHandler).handleStatus(thermostatStatus));
336 } else if (status instanceof ExtendedAudioZoneStatus audioZoneStatus) {
337 int audioZoneNumber = audioZoneStatus.getNumber();
339 logger.debug("Received status update for Audio Zone: {}, status: {}", audioZoneNumber,
341 Optional<Thing> theThing = getChildThing(THING_TYPE_AUDIO_ZONE, audioZoneNumber);
342 theThing.map(Thing::getHandler)
343 .ifPresent(theHandler -> ((AudioZoneHandler) theHandler).handleStatus(audioZoneStatus));
344 } else if (status instanceof ExtendedAuxSensorStatus auxSensorStatus) {
345 int auxSensorNumber = auxSensorStatus.getNumber();
347 // Aux Sensors can be either temperature or humidity, need to check both.
348 Optional<Thing> tempThing = getChildThing(THING_TYPE_TEMP_SENSOR, auxSensorNumber);
349 Optional<Thing> humidityThing = getChildThing(THING_TYPE_HUMIDITY_SENSOR, auxSensorNumber);
350 if (tempThing.isPresent()) {
351 logger.debug("Received status update for Temperature Sensor: {}, status: {}", auxSensorNumber,
353 tempThing.map(Thing::getHandler).ifPresent(
354 theHandler -> ((TempSensorHandler) theHandler).handleStatus(auxSensorStatus));
356 if (humidityThing.isPresent()) {
357 logger.debug("Received status update for Humidity Sensor: {}, status: {}", auxSensorNumber,
359 humidityThing.map(Thing::getHandler).ifPresent(
360 theHandler -> ((HumiditySensorHandler) theHandler).handleStatus(auxSensorStatus));
363 logger.debug("Received Object Status Notification that was not processed: {}", objectStatus);
367 logger.debug("Received null Object Status Notification!");
372 public void systemEventNotification(@Nullable SystemEvent event) {
374 logger.debug("Received System Event Notification of type: {}", event.getType());
375 switch (event.getType()) {
376 case PHONE_LINE_DEAD:
377 case PHONE_LINE_OFF_HOOK:
378 case PHONE_LINE_ON_HOOK:
379 case PHONE_LINE_RING:
380 ChannelUID channel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_PHONE_LINE_EVENT);
381 triggerChannel(channel, event.getType().toString().replaceAll("^PHONE_LINE_", ""));
384 case AC_POWER_RESTORED:
385 ChannelUID acChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_AC_POWER_EVENT);
386 triggerChannel(acChannel, event.getType().toString().replaceAll("^AC_POWER_", ""));
390 ChannelUID batteryChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_BATTERY_EVENT);
391 triggerChannel(batteryChannel, event.getType().toString().replaceAll("^BATTERY_", ""));
395 ChannelUID dcmChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_DCM_EVENT);
396 triggerChannel(dcmChannel, event.getType().toString().replaceAll("^DCM_", ""));
398 case ENERGY_COST_CRITICAL:
399 case ENERGY_COST_HIGH:
400 case ENERGY_COST_LOW:
401 case ENERGY_COST_MID:
402 ChannelUID energyChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_ENERGY_COST_EVENT);
403 triggerChannel(energyChannel, event.getType().toString().replaceAll("^ENERGY_COST_", ""));
405 case CAMERA_1_TRIGGER:
406 case CAMERA_2_TRIGGER:
407 case CAMERA_3_TRIGGER:
408 case CAMERA_4_TRIGGER:
409 case CAMERA_5_TRIGGER:
410 case CAMERA_6_TRIGGER:
411 ChannelUID cameraChannel = new ChannelUID(getThing().getUID(),
412 TRIGGER_CHANNEL_CAMERA_TRIGGER_EVENT);
413 triggerChannel(cameraChannel, String.valueOf(event.getType().toString().charAt(8)));
416 Optional<Thing> buttonThing = getChildThing(THING_TYPE_BUTTON,
417 ((ButtonEvent) event).getButtonNumber());
418 buttonThing.map(Thing::getHandler)
419 .ifPresent(theHandler -> ((ButtonHandler) theHandler).buttonActivated());
422 Optional<Thing> areaThing = getChildThing(THING_TYPE_OMNI_AREA, ((AllOnOffEvent) event).getArea());
423 if (areaThing.isPresent()) {
424 logger.debug("Thing for allOnOff event: {}", areaThing.get().getUID());
425 areaThing.map(Thing::getHandler).ifPresent(theHandler -> ((AbstractAreaHandler) theHandler)
426 .handleAllOnOffEvent((AllOnOffEvent) event));
430 UPBLinkEvent linkEvent = (UPBLinkEvent) event;
431 UPBLinkEvent.Command command = linkEvent.getLinkCommand();
432 int link = linkEvent.getLinkNumber();
433 handleUPBLink(link, command);
435 case ALC_UPB_RADIORA_STARLITE_SWITCH_PRESS:
436 SwitchPressEvent switchPressEvent = (SwitchPressEvent) event;
437 int unitNumber = switchPressEvent.getUnitNumber();
439 Optional<Thing> unitThing = getUnitThing(unitNumber);
440 unitThing.map(Thing::getHandler).ifPresent(
441 theHandler -> ((UnitHandler) theHandler).handleSwitchPressEvent(switchPressEvent));
444 logger.warn("Ignoring System Event Notification of type: {}", event.getType());
447 logger.debug("Received null System Event Notification!");
451 private void handleUPBLink(int link, UPBLinkEvent.Command command) {
452 final ChannelUID activateChannel;
454 if (command == UPBLinkEvent.Command.ACTIVATED) {
455 activateChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_UPB_LINK_ACTIVATED_EVENT);
456 } else if (command == UPBLinkEvent.Command.DEACTIVATED) {
457 activateChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_UPB_LINK_DEACTIVATED_EVENT);
459 logger.debug("Received unsupported UPB link event: {}", command);
462 triggerChannel(activateChannel, Integer.toString(link));
466 public void notConnectedEvent(@Nullable Exception e) {
468 logger.debug("Received an OmniLink Controller not connected event: {}", e.getMessage());
469 setOfflineAndReconnect(e.getMessage());
473 private void getSystemStatus() throws IOException, OmniNotConnectedException, OmniInvalidResponseException,
474 OmniUnknownMessageTypeException {
475 SystemStatus status = getOmniConnection().reqSystemStatus();
476 logger.debug("Received system status: {}", status);
477 // Update controller's reported time
478 String dateString = new StringBuilder().append(2000 + status.getYear()).append("-")
479 .append(String.format("%02d", status.getMonth())).append("-")
480 .append(String.format("%02d", status.getDay())).append("T")
481 .append(String.format("%02d", status.getHour())).append(":")
482 .append(String.format("%02d", status.getMinute())).append(":")
483 .append(String.format("%02d", status.getSecond())).toString();
484 updateState(CHANNEL_SYSTEM_DATE, new DateTimeType(dateString));
487 public Message reqObjectProperties(int objectType, int objectNum, int direction, int filter1, int filter2,
488 int filter3) throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
490 return getOmniConnection().reqObjectProperties(objectType, objectNum, direction, filter1, filter2, filter3);
491 } catch (OmniNotConnectedException | IOException e) {
492 setOfflineAndReconnect(e.getMessage());
493 throw new BridgeOfflineException(e);
497 public Message requestAudioSourceStatus(final int source, final int position)
498 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
500 return getOmniConnection().reqAudioSourceStatus(source, position);
501 } catch (OmniNotConnectedException | IOException e) {
502 setOfflineAndReconnect(e.getMessage());
503 throw new BridgeOfflineException(e);
507 public ObjectStatus requestObjectStatus(final int objType, final int startObject, final int endObject,
509 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
511 return getOmniConnection().reqObjectStatus(objType, startObject, endObject, extended);
512 } catch (OmniNotConnectedException | IOException e) {
513 setOfflineAndReconnect(e.getMessage());
514 throw new BridgeOfflineException(e);
518 public Optional<TemperatureFormat> getTemperatureFormat() {
520 return Optional.of(TemperatureFormat.valueOf(reqSystemFormats().getTempFormat()));
521 } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
522 logger.debug("Could not request temperature format from controller: {}", e.getMessage());
523 return Optional.empty();
527 public void updateChannels() {
530 updateState(CHANNEL_CONSOLE_ENABLE_DISABLE_BEEPER, UnDefType.UNDEF);
531 updateState(CHANNEL_CONSOLE_BEEP, UnDefType.UNDEF);
532 } catch (IOException | OmniNotConnectedException | OmniInvalidResponseException
533 | OmniUnknownMessageTypeException e) {
534 logger.warn("Unable to update bridge channels: {}", e.getMessage());
539 public void dispose() {
540 cancelReconnectJob(true);
541 cancelEventPolling();
542 final Connection connection = omniConnection;
543 if (connection != null) {
544 connection.removeDisconnectListener(this);
545 connection.disconnect();
549 private Optional<Thing> getChildThing(ThingTypeUID type, int number) {
550 Bridge bridge = getThing();
551 return bridge.getThings().stream().filter(t -> t.getThingTypeUID().equals(type))
552 .filter(t -> ((Number) t.getConfiguration().get(THING_PROPERTIES_NUMBER)).intValue() == number)
556 private Optional<Thing> getUnitThing(int unitId) {
557 return SUPPORTED_UNIT_TYPES_UIDS.stream().map(uid -> getChildThing(uid, unitId)).flatMap(Optional::stream)
561 public Optional<AudioPlayer> getAudioPlayer() {
565 public Message readEventRecord(int eventNumber, int direction)
566 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
568 return getOmniConnection().readEventRecord(eventNumber, direction);
569 } catch (OmniNotConnectedException | IOException e) {
570 setOfflineAndReconnect(e.getMessage());
571 throw new BridgeOfflineException(e);
575 private void updateBridgeProperties() {
577 SystemInformation systemInformation = reqSystemInformation();
578 Map<String, String> properties = editProperties();
579 properties.put(Thing.PROPERTY_MODEL_ID, Integer.toString(systemInformation.getModel()));
580 properties.put(Thing.PROPERTY_FIRMWARE_VERSION,
581 Integer.toString(systemInformation.getMajor()) + "."
582 + Integer.toString(systemInformation.getMinor()) + "."
583 + Integer.toString(systemInformation.getRevision()));
584 properties.put(THING_PROPERTIES_PHONE_NUMBER, systemInformation.getPhone());
585 updateProperties(properties);
586 } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
587 logger.debug("Could not request system information from OmniLink Controller: {}", e.getMessage());
592 public void initialize() {
593 scheduleReconnectJob();
596 private void scheduleReconnectJob() {
597 ScheduledFuture<?> currentReconnectJob = connectJob;
598 if (currentReconnectJob == null || currentReconnectJob.isDone()) {
599 connectJob = super.scheduler.scheduleWithFixedDelay(this::makeOmnilinkConnection, 0, autoReconnectPeriod,
604 private void cancelReconnectJob(boolean kill) {
605 ScheduledFuture<?> currentReconnectJob = connectJob;
606 if (currentReconnectJob != null) {
607 currentReconnectJob.cancel(kill);
611 private void setOfflineAndReconnect(@Nullable String message) {
612 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message);
613 cancelEventPolling();
614 final Connection connection = omniConnection;
615 if (connection != null) {
616 connection.removeDisconnectListener(this);
618 scheduleReconnectJob();
621 private void startEventPolling(int interval) {
622 ScheduledFuture<?> eventPollingJobFuture = eventPollingJob;
623 if (eventPollingJobFuture == null || eventPollingJobFuture.isDone()) {
625 eventPollingJob = super.scheduler.scheduleWithFixedDelay(this::pollEvents, 0, interval, TimeUnit.SECONDS);
629 private void cancelEventPolling() {
630 ScheduledFuture<?> eventPollingJobFuture = eventPollingJob;
631 if (eventPollingJobFuture != null) {
632 eventPollingJobFuture.cancel(true);
636 private void pollEvents() {
637 // On first run, direction is -1 (most recent event), after its 1 for the next log message
641 logger.trace("Polling for event log messages.");
642 int direction = eventLogNumber == 0 ? -1 : 1;
643 message = readEventRecord(eventLogNumber, direction);
644 if (message.getMessageType() == Message.MESG_TYPE_EVENT_LOG_DATA) {
645 EventLogData logData = (EventLogData) message;
646 logger.debug("Processing event log message number: {}", logData.getEventNumber());
647 eventLogNumber = logData.getEventNumber();
648 String json = gson.toJson(logData);
649 logger.debug("Receieved event log message: {}", json);
650 updateState(CHANNEL_EVENT_LOG, new StringType(json));
652 } while (message.getMessageType() != Message.MESG_TYPE_END_OF_DATA);
654 } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
655 logger.debug("Exception recieved while polling for event log messages: {}", e.getMessage());
659 private Connection getOmniConnection() throws OmniNotConnectedException {
660 final Connection connection = omniConnection;
661 if (connection != null) {
664 throw new OmniNotConnectedException("Connection not yet established!");