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) {
196 sendOmnilinkCommand(CommandMessage.CMD_CONSOLE_ENABLE_DISABLE_BEEPER,
197 ((StringType) command).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) {
210 sendOmnilinkCommand(CommandMessage.CMD_CONSOLE_BEEP, ((DecimalType) command).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) {
291 ExtendedUnitStatus unitStatus = (ExtendedUnitStatus) status;
292 int unitNumber = unitStatus.getNumber();
294 logger.debug("Received status update for Unit: {}, status: {}", unitNumber, unitStatus);
295 Optional<Thing> theThing = getUnitThing(unitNumber);
296 theThing.map(Thing::getHandler)
297 .ifPresent(theHandler -> ((UnitHandler) theHandler).handleStatus(unitStatus));
298 } else if (status instanceof ExtendedZoneStatus) {
299 ExtendedZoneStatus zoneStatus = (ExtendedZoneStatus) status;
300 int zoneNumber = zoneStatus.getNumber();
302 logger.debug("Received status update for Zone: {}, status: {}", zoneNumber, zoneStatus);
303 Optional<Thing> theThing = getChildThing(THING_TYPE_ZONE, zoneNumber);
304 theThing.map(Thing::getHandler)
305 .ifPresent(theHandler -> ((ZoneHandler) theHandler).handleStatus(zoneStatus));
306 } else if (status instanceof ExtendedAreaStatus) {
307 ExtendedAreaStatus areaStatus = (ExtendedAreaStatus) status;
308 int areaNumber = areaStatus.getNumber();
310 logger.debug("Received status update for Area: {}, status: {}", areaNumber, areaStatus);
311 systemType.ifPresent(t -> {
312 Optional<Thing> theThing = Optional.empty();
315 theThing = getChildThing(THING_TYPE_LUMINA_AREA, areaNumber);
318 theThing = getChildThing(THING_TYPE_OMNI_AREA, areaNumber);
321 theThing.map(Thing::getHandler)
322 .ifPresent(theHandler -> ((AbstractAreaHandler) theHandler).handleStatus(areaStatus));
324 } else if (status instanceof ExtendedAccessControlReaderLockStatus) {
325 ExtendedAccessControlReaderLockStatus lockStatus = (ExtendedAccessControlReaderLockStatus) status;
326 int lockNumber = lockStatus.getNumber();
328 logger.debug("Received status update for Lock: {}, status: {}", lockNumber, lockStatus);
329 Optional<Thing> theThing = getChildThing(THING_TYPE_LOCK, lockNumber);
330 theThing.map(Thing::getHandler)
331 .ifPresent(theHandler -> ((LockHandler) theHandler).handleStatus(lockStatus));
332 } else if (status instanceof ExtendedThermostatStatus) {
333 ExtendedThermostatStatus thermostatStatus = (ExtendedThermostatStatus) status;
334 int thermostatNumber = thermostatStatus.getNumber();
336 logger.debug("Received status update for Thermostat: {}, status: {}", thermostatNumber,
338 Optional<Thing> theThing = getChildThing(THING_TYPE_THERMOSTAT, thermostatNumber);
339 theThing.map(Thing::getHandler)
340 .ifPresent(theHandler -> ((ThermostatHandler) theHandler).handleStatus(thermostatStatus));
341 } else if (status instanceof ExtendedAudioZoneStatus) {
342 ExtendedAudioZoneStatus audioZoneStatus = (ExtendedAudioZoneStatus) status;
343 int audioZoneNumber = audioZoneStatus.getNumber();
345 logger.debug("Received status update for Audio Zone: {}, status: {}", audioZoneNumber,
347 Optional<Thing> theThing = getChildThing(THING_TYPE_AUDIO_ZONE, audioZoneNumber);
348 theThing.map(Thing::getHandler)
349 .ifPresent(theHandler -> ((AudioZoneHandler) theHandler).handleStatus(audioZoneStatus));
350 } else if (status instanceof ExtendedAuxSensorStatus) {
351 ExtendedAuxSensorStatus auxSensorStatus = (ExtendedAuxSensorStatus) status;
352 int auxSensorNumber = auxSensorStatus.getNumber();
354 // Aux Sensors can be either temperature or humidity, need to check both.
355 Optional<Thing> tempThing = getChildThing(THING_TYPE_TEMP_SENSOR, auxSensorNumber);
356 Optional<Thing> humidityThing = getChildThing(THING_TYPE_HUMIDITY_SENSOR, auxSensorNumber);
357 if (tempThing.isPresent()) {
358 logger.debug("Received status update for Temperature Sensor: {}, status: {}", auxSensorNumber,
360 tempThing.map(Thing::getHandler).ifPresent(
361 theHandler -> ((TempSensorHandler) theHandler).handleStatus(auxSensorStatus));
363 if (humidityThing.isPresent()) {
364 logger.debug("Received status update for Humidity Sensor: {}, status: {}", auxSensorNumber,
366 humidityThing.map(Thing::getHandler).ifPresent(
367 theHandler -> ((HumiditySensorHandler) theHandler).handleStatus(auxSensorStatus));
370 logger.debug("Received Object Status Notification that was not processed: {}", objectStatus);
374 logger.debug("Received null Object Status Notification!");
379 public void systemEventNotification(@Nullable SystemEvent event) {
381 logger.debug("Received System Event Notification of type: {}", event.getType());
382 switch (event.getType()) {
383 case PHONE_LINE_DEAD:
384 case PHONE_LINE_OFF_HOOK:
385 case PHONE_LINE_ON_HOOK:
386 case PHONE_LINE_RING:
387 ChannelUID channel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_PHONE_LINE_EVENT);
388 triggerChannel(channel, event.getType().toString().replaceAll("^PHONE_LINE_", ""));
391 case AC_POWER_RESTORED:
392 ChannelUID acChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_AC_POWER_EVENT);
393 triggerChannel(acChannel, event.getType().toString().replaceAll("^AC_POWER_", ""));
397 ChannelUID batteryChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_BATTERY_EVENT);
398 triggerChannel(batteryChannel, event.getType().toString().replaceAll("^BATTERY_", ""));
402 ChannelUID dcmChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_DCM_EVENT);
403 triggerChannel(dcmChannel, event.getType().toString().replaceAll("^DCM_", ""));
405 case ENERGY_COST_CRITICAL:
406 case ENERGY_COST_HIGH:
407 case ENERGY_COST_LOW:
408 case ENERGY_COST_MID:
409 ChannelUID energyChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_ENERGY_COST_EVENT);
410 triggerChannel(energyChannel, event.getType().toString().replaceAll("^ENERGY_COST_", ""));
412 case CAMERA_1_TRIGGER:
413 case CAMERA_2_TRIGGER:
414 case CAMERA_3_TRIGGER:
415 case CAMERA_4_TRIGGER:
416 case CAMERA_5_TRIGGER:
417 case CAMERA_6_TRIGGER:
418 ChannelUID cameraChannel = new ChannelUID(getThing().getUID(),
419 TRIGGER_CHANNEL_CAMERA_TRIGGER_EVENT);
420 triggerChannel(cameraChannel, String.valueOf(event.getType().toString().charAt(8)));
423 Optional<Thing> buttonThing = getChildThing(THING_TYPE_BUTTON,
424 ((ButtonEvent) event).getButtonNumber());
425 buttonThing.map(Thing::getHandler)
426 .ifPresent(theHandler -> ((ButtonHandler) theHandler).buttonActivated());
429 Optional<Thing> areaThing = getChildThing(THING_TYPE_OMNI_AREA, ((AllOnOffEvent) event).getArea());
430 if (areaThing.isPresent()) {
431 logger.debug("Thing for allOnOff event: {}", areaThing.get().getUID());
432 areaThing.map(Thing::getHandler).ifPresent(theHandler -> ((AbstractAreaHandler) theHandler)
433 .handleAllOnOffEvent((AllOnOffEvent) event));
437 UPBLinkEvent linkEvent = (UPBLinkEvent) event;
438 UPBLinkEvent.Command command = linkEvent.getLinkCommand();
439 int link = linkEvent.getLinkNumber();
440 handleUPBLink(link, command);
442 case ALC_UPB_RADIORA_STARLITE_SWITCH_PRESS:
443 SwitchPressEvent switchPressEvent = (SwitchPressEvent) event;
444 int unitNumber = switchPressEvent.getUnitNumber();
446 Optional<Thing> unitThing = getUnitThing(unitNumber);
447 unitThing.map(Thing::getHandler).ifPresent(
448 theHandler -> ((UnitHandler) theHandler).handleSwitchPressEvent(switchPressEvent));
451 logger.warn("Ignoring System Event Notification of type: {}", event.getType());
454 logger.debug("Received null System Event Notification!");
458 private void handleUPBLink(int link, UPBLinkEvent.Command command) {
459 final ChannelUID activateChannel;
461 if (command == UPBLinkEvent.Command.ACTIVATED) {
462 activateChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_UPB_LINK_ACTIVATED_EVENT);
463 } else if (command == UPBLinkEvent.Command.DEACTIVATED) {
464 activateChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_UPB_LINK_DEACTIVATED_EVENT);
466 logger.debug("Received unsupported UPB link event: {}", command);
469 triggerChannel(activateChannel, Integer.toString(link));
473 public void notConnectedEvent(@Nullable Exception e) {
475 logger.debug("Received an OmniLink Controller not connected event: {}", e.getMessage());
476 setOfflineAndReconnect(e.getMessage());
480 private void getSystemStatus() throws IOException, OmniNotConnectedException, OmniInvalidResponseException,
481 OmniUnknownMessageTypeException {
482 SystemStatus status = getOmniConnection().reqSystemStatus();
483 logger.debug("Received system status: {}", status);
484 // Update controller's reported time
485 String dateString = new StringBuilder().append(2000 + status.getYear()).append("-")
486 .append(String.format("%02d", status.getMonth())).append("-")
487 .append(String.format("%02d", status.getDay())).append("T")
488 .append(String.format("%02d", status.getHour())).append(":")
489 .append(String.format("%02d", status.getMinute())).append(":")
490 .append(String.format("%02d", status.getSecond())).toString();
491 updateState(CHANNEL_SYSTEM_DATE, new DateTimeType(dateString));
494 public Message reqObjectProperties(int objectType, int objectNum, int direction, int filter1, int filter2,
495 int filter3) throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
497 return getOmniConnection().reqObjectProperties(objectType, objectNum, direction, filter1, filter2, filter3);
498 } catch (OmniNotConnectedException | IOException e) {
499 setOfflineAndReconnect(e.getMessage());
500 throw new BridgeOfflineException(e);
504 public Message requestAudioSourceStatus(final int source, final int position)
505 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
507 return getOmniConnection().reqAudioSourceStatus(source, position);
508 } catch (OmniNotConnectedException | IOException e) {
509 setOfflineAndReconnect(e.getMessage());
510 throw new BridgeOfflineException(e);
514 public ObjectStatus requestObjectStatus(final int objType, final int startObject, final int endObject,
516 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
518 return getOmniConnection().reqObjectStatus(objType, startObject, endObject, extended);
519 } catch (OmniNotConnectedException | IOException e) {
520 setOfflineAndReconnect(e.getMessage());
521 throw new BridgeOfflineException(e);
525 public Optional<TemperatureFormat> getTemperatureFormat() {
527 return Optional.of(TemperatureFormat.valueOf(reqSystemFormats().getTempFormat()));
528 } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
529 logger.debug("Could not request temperature format from controller: {}", e.getMessage());
530 return Optional.empty();
534 public void updateChannels() {
537 updateState(CHANNEL_CONSOLE_ENABLE_DISABLE_BEEPER, UnDefType.UNDEF);
538 updateState(CHANNEL_CONSOLE_BEEP, UnDefType.UNDEF);
539 } catch (IOException | OmniNotConnectedException | OmniInvalidResponseException
540 | OmniUnknownMessageTypeException e) {
541 logger.warn("Unable to update bridge channels: {}", e.getMessage());
546 public void dispose() {
547 cancelReconnectJob(true);
548 cancelEventPolling();
549 final Connection connection = omniConnection;
550 if (connection != null) {
551 connection.removeDisconnectListener(this);
552 connection.disconnect();
556 private Optional<Thing> getChildThing(ThingTypeUID type, int number) {
557 Bridge bridge = getThing();
558 return bridge.getThings().stream().filter(t -> t.getThingTypeUID().equals(type))
559 .filter(t -> ((Number) t.getConfiguration().get(THING_PROPERTIES_NUMBER)).intValue() == number)
563 private Optional<Thing> getUnitThing(int unitId) {
564 return SUPPORTED_UNIT_TYPES_UIDS.stream().map(uid -> getChildThing(uid, unitId)).flatMap(Optional::stream)
568 public Optional<AudioPlayer> getAudioPlayer() {
572 public Message readEventRecord(int eventNumber, int direction)
573 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
575 return getOmniConnection().readEventRecord(eventNumber, direction);
576 } catch (OmniNotConnectedException | IOException e) {
577 setOfflineAndReconnect(e.getMessage());
578 throw new BridgeOfflineException(e);
582 private void updateBridgeProperties() {
584 SystemInformation systemInformation = reqSystemInformation();
585 Map<String, String> properties = editProperties();
586 properties.put(Thing.PROPERTY_MODEL_ID, Integer.toString(systemInformation.getModel()));
587 properties.put(Thing.PROPERTY_FIRMWARE_VERSION,
588 Integer.toString(systemInformation.getMajor()) + "."
589 + Integer.toString(systemInformation.getMinor()) + "."
590 + Integer.toString(systemInformation.getRevision()));
591 properties.put(THING_PROPERTIES_PHONE_NUMBER, systemInformation.getPhone());
592 updateProperties(properties);
593 } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
594 logger.debug("Could not request system information from OmniLink Controller: {}", e.getMessage());
599 public void initialize() {
600 scheduleReconnectJob();
603 private void scheduleReconnectJob() {
604 ScheduledFuture<?> currentReconnectJob = connectJob;
605 if (currentReconnectJob == null || currentReconnectJob.isDone()) {
606 connectJob = super.scheduler.scheduleWithFixedDelay(this::makeOmnilinkConnection, 0, autoReconnectPeriod,
611 private void cancelReconnectJob(boolean kill) {
612 ScheduledFuture<?> currentReconnectJob = connectJob;
613 if (currentReconnectJob != null) {
614 currentReconnectJob.cancel(kill);
618 private void setOfflineAndReconnect(@Nullable String message) {
619 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message);
620 cancelEventPolling();
621 final Connection connection = omniConnection;
622 if (connection != null) {
623 connection.removeDisconnectListener(this);
625 scheduleReconnectJob();
628 private void startEventPolling(int interval) {
629 ScheduledFuture<?> eventPollingJobFuture = eventPollingJob;
630 if (eventPollingJobFuture == null || eventPollingJobFuture.isDone()) {
632 eventPollingJob = super.scheduler.scheduleWithFixedDelay(this::pollEvents, 0, interval, TimeUnit.SECONDS);
636 private void cancelEventPolling() {
637 ScheduledFuture<?> eventPollingJobFuture = eventPollingJob;
638 if (eventPollingJobFuture != null) {
639 eventPollingJobFuture.cancel(true);
643 private void pollEvents() {
644 // On first run, direction is -1 (most recent event), after its 1 for the next log message
648 logger.trace("Polling for event log messages.");
649 int direction = eventLogNumber == 0 ? -1 : 1;
650 message = readEventRecord(eventLogNumber, direction);
651 if (message.getMessageType() == Message.MESG_TYPE_EVENT_LOG_DATA) {
652 EventLogData logData = (EventLogData) message;
653 logger.debug("Processing event log message number: {}", logData.getEventNumber());
654 eventLogNumber = logData.getEventNumber();
655 String json = gson.toJson(logData);
656 logger.debug("Receieved event log message: {}", json);
657 updateState(CHANNEL_EVENT_LOG, new StringType(json));
659 } while (message.getMessageType() != Message.MESG_TYPE_END_OF_DATA);
661 } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
662 logger.debug("Exception recieved while polling for event log messages: {}", e.getMessage());
666 private Connection getOmniConnection() throws OmniNotConnectedException {
667 final Connection connection = omniConnection;
668 if (connection != null) {
671 throw new OmniNotConnectedException("Connection not yet established!");