2 * Copyright (c) 2010-2021 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;
21 import java.util.Collections;
23 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.config.OmnilinkBridgeConfig;
32 import org.openhab.binding.omnilink.internal.discovery.OmnilinkDiscoveryService;
33 import org.openhab.core.library.types.DateTimeType;
34 import org.openhab.core.library.types.DecimalType;
35 import org.openhab.core.library.types.StringType;
36 import org.openhab.core.thing.Bridge;
37 import org.openhab.core.thing.ChannelUID;
38 import org.openhab.core.thing.Thing;
39 import org.openhab.core.thing.ThingStatus;
40 import org.openhab.core.thing.ThingStatusDetail;
41 import org.openhab.core.thing.ThingTypeUID;
42 import org.openhab.core.thing.binding.BaseBridgeHandler;
43 import org.openhab.core.thing.binding.ThingHandlerService;
44 import org.openhab.core.types.Command;
45 import org.openhab.core.types.RefreshType;
46 import org.openhab.core.types.UnDefType;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
50 import com.digitaldan.jomnilinkII.Connection;
51 import com.digitaldan.jomnilinkII.DisconnectListener;
52 import com.digitaldan.jomnilinkII.Message;
53 import com.digitaldan.jomnilinkII.MessageTypes.EventLogData;
54 import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
55 import com.digitaldan.jomnilinkII.MessageTypes.SecurityCodeValidation;
56 import com.digitaldan.jomnilinkII.MessageTypes.SystemFeatures;
57 import com.digitaldan.jomnilinkII.MessageTypes.SystemFormats;
58 import com.digitaldan.jomnilinkII.MessageTypes.SystemInformation;
59 import com.digitaldan.jomnilinkII.MessageTypes.SystemStatus;
60 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAccessControlReaderLockStatus;
61 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAreaStatus;
62 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAudioZoneStatus;
63 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAuxSensorStatus;
64 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedThermostatStatus;
65 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedUnitStatus;
66 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedZoneStatus;
67 import com.digitaldan.jomnilinkII.MessageTypes.statuses.Status;
68 import com.digitaldan.jomnilinkII.MessageTypes.systemevents.AllOnOffEvent;
69 import com.digitaldan.jomnilinkII.MessageTypes.systemevents.ButtonEvent;
70 import com.digitaldan.jomnilinkII.MessageTypes.systemevents.SwitchPressEvent;
71 import com.digitaldan.jomnilinkII.MessageTypes.systemevents.SystemEvent;
72 import com.digitaldan.jomnilinkII.MessageTypes.systemevents.UPBLinkEvent;
73 import com.digitaldan.jomnilinkII.NotificationListener;
74 import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
75 import com.digitaldan.jomnilinkII.OmniNotConnectedException;
76 import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
77 import com.google.gson.Gson;
80 * The {@link OmnilinkBridgeHandler} defines some methods that are used to
81 * interface with an OmniLink Controller. This by extension also defines the
82 * OmniLink bridge that openHAB will be able to pick up and interface with.
84 * @author Craig Hamilton - Initial contribution
85 * @author Ethan Dye - openHAB3 rewrite
88 public class OmnilinkBridgeHandler extends BaseBridgeHandler implements NotificationListener, DisconnectListener {
89 private final Logger logger = LoggerFactory.getLogger(OmnilinkBridgeHandler.class);
90 private @Nullable Connection omniConnection = null;
91 private @Nullable ScheduledFuture<?> connectJob;
92 private @Nullable ScheduledFuture<?> eventPollingJob;
93 private final int autoReconnectPeriod = 60;
94 private Optional<AudioPlayer> audioPlayer = Optional.empty();
95 private @Nullable SystemType systemType = null;
96 private final Gson gson = new Gson();
97 private int eventLogNumber = 0;
99 public OmnilinkBridgeHandler(Bridge bridge) {
104 public Collection<Class<? extends ThingHandlerService>> getServices() {
105 return Collections.singleton(OmnilinkDiscoveryService.class);
108 public void sendOmnilinkCommand(final int message, final int param1, final int param2)
109 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
111 getOmniConnection().controllerCommand(message, param1, param2);
112 } catch (IOException | OmniNotConnectedException e) {
113 setOfflineAndReconnect(e.getMessage());
114 throw new BridgeOfflineException(e);
118 public SecurityCodeValidation reqSecurityCodeValidation(int area, int digit1, int digit2, int digit3, int digit4)
119 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
121 return getOmniConnection().reqSecurityCodeValidation(area, digit1, digit2, digit3, digit4);
122 } catch (IOException | OmniNotConnectedException e) {
123 setOfflineAndReconnect(e.getMessage());
124 throw new BridgeOfflineException(e);
128 public void activateKeypadEmergency(int area, int emergencyType)
129 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
131 getOmniConnection().activateKeypadEmergency(area, emergencyType);
132 } catch (IOException | OmniNotConnectedException e) {
133 setOfflineAndReconnect(e.getMessage());
134 throw new BridgeOfflineException(e);
138 public SystemInformation reqSystemInformation()
139 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
141 return getOmniConnection().reqSystemInformation();
142 } catch (IOException | OmniNotConnectedException e) {
143 setOfflineAndReconnect(e.getMessage());
144 throw new BridgeOfflineException(e);
148 public SystemFormats reqSystemFormats()
149 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
151 return getOmniConnection().reqSystemFormats();
152 } catch (IOException | OmniNotConnectedException e) {
153 setOfflineAndReconnect(e.getMessage());
154 throw new BridgeOfflineException(e);
158 private SystemFeatures reqSystemFeatures()
159 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
161 return getOmniConnection().reqSystemFeatures();
162 } catch (IOException | OmniNotConnectedException e) {
163 setOfflineAndReconnect(e.getMessage());
164 throw new BridgeOfflineException(e);
169 public void handleCommand(ChannelUID channelUID, Command command) {
170 logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
172 if (command instanceof RefreshType) {
177 switch (channelUID.getId()) {
178 case CHANNEL_SYSTEMDATE:
179 if (command instanceof DateTimeType) {
180 ZonedDateTime zdt = ((DateTimeType) command).getZonedDateTime();
181 boolean inDaylightSavings = zdt.getZone().getRules().isDaylightSavings(zdt.toInstant());
183 getOmniConnection().setTimeCommand(zdt.getYear() - 2000, zdt.getMonthValue(),
184 zdt.getDayOfMonth(), zdt.getDayOfWeek().getValue(), zdt.getHour(), zdt.getMinute(),
186 } catch (IOException | OmniNotConnectedException | OmniInvalidResponseException
187 | OmniUnknownMessageTypeException e) {
188 logger.debug("Could not send Set Time command to OmniLink Controller: {}", e.getMessage());
191 logger.debug("Invalid command: {}, must be DateTimeType", command);
194 case CHANNEL_CONSOLE_ENABLE_DISABLE_BEEPER:
195 if (command instanceof StringType) {
197 sendOmnilinkCommand(OmniLinkCmd.CMD_CONSOLE_ENABLE_DISABLE_BEEPER.getNumber(),
198 ((StringType) command).equals(StringType.valueOf("OFF")) ? 0 : 1, 0);
199 updateState(CHANNEL_CONSOLE_ENABLE_DISABLE_BEEPER, UnDefType.UNDEF);
200 } catch (NumberFormatException | OmniInvalidResponseException | OmniUnknownMessageTypeException
201 | BridgeOfflineException e) {
202 logger.debug("Could not send Console command to OmniLink Controller: {}", e.getMessage());
205 logger.debug("Invalid command: {}, must be StringType", command);
208 case CHANNEL_CONSOLE_BEEP:
209 if (command instanceof DecimalType) {
211 sendOmnilinkCommand(OmniLinkCmd.CMD_CONSOLE_BEEP.getNumber(),
212 ((DecimalType) command).intValue(), 0);
213 updateState(CHANNEL_CONSOLE_BEEP, UnDefType.UNDEF);
214 } catch (NumberFormatException | OmniInvalidResponseException | OmniUnknownMessageTypeException
215 | BridgeOfflineException e) {
216 logger.debug("Could not send Console command to OmniLink Controller: {}", e.getMessage());
219 logger.debug("Invalid command: {}, must be DecimalType", command);
223 logger.warn("Unknown channel for Bridge thing: {}", channelUID);
227 private void makeOmnilinkConnection() {
228 final Connection connection = omniConnection;
229 if (connection != null && connection.connected()) {
233 logger.debug("Attempting to connect to controller!");
235 OmnilinkBridgeConfig config = getConfigAs(OmnilinkBridgeConfig.class);
237 this.omniConnection = new Connection(config.getIpAddress(), config.getPort(),
238 config.getKey1() + ":" + config.getKey2());
241 * HAI only supports one audio player - cycle through features until we find a feature that is an audio
244 audioPlayer = reqSystemFeatures().getFeatures().stream()
245 .map(featureCode -> AudioPlayer.getAudioPlayerForFeatureCode(featureCode))
246 .filter(Optional::isPresent).findFirst().orElse(Optional.empty());
248 systemType = SystemType.getType(reqSystemInformation().getModel());
250 if (config.getLogPollingInterval() > 0) {
251 startEventPolling(config.getLogPollingInterval());
254 final Connection connectionNew = omniConnection;
255 if (connectionNew != null) {
256 connectionNew.enableNotifications();
257 connectionNew.addNotificationListener(OmnilinkBridgeHandler.this);
258 connectionNew.addDisconnectListener(this);
261 updateStatus(ThingStatus.ONLINE);
262 cancelReconnectJob(false);
264 updateBridgeProperties();
265 } catch (UnknownHostException e) {
266 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
267 } catch (IOException e) {
268 final Throwable cause = e.getCause();
270 final String causeMessage = cause.getMessage();
272 if (causeMessage != null && causeMessage.contains("Connection timed out")) {
273 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
274 "IP Address probably incorrect, timed out creating connection!");
276 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, causeMessage);
279 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
281 } catch (Exception e) {
282 setOfflineAndReconnect(e.getMessage());
283 logger.debug("Error connecting to OmniLink Controller: {}", e.getMessage());
288 public void objectStatusNotification(@Nullable ObjectStatus objectStatus) {
289 if (objectStatus != null) {
290 Status[] statuses = objectStatus.getStatuses();
291 for (Status status : statuses) {
292 if (status instanceof ExtendedUnitStatus) {
293 ExtendedUnitStatus unitStatus = (ExtendedUnitStatus) status;
294 int unitNumber = unitStatus.getNumber();
296 logger.debug("Received status update for Unit: {}, status: {}", unitNumber, unitStatus);
297 Optional<Thing> theThing = getUnitThing(unitNumber);
298 theThing.map(Thing::getHandler)
299 .ifPresent(theHandler -> ((UnitHandler) theHandler).handleStatus(unitStatus));
300 } else if (status instanceof ExtendedZoneStatus) {
301 ExtendedZoneStatus zoneStatus = (ExtendedZoneStatus) status;
302 int zoneNumber = zoneStatus.getNumber();
304 logger.debug("Received status update for Zone: {}, status: {}", zoneNumber, zoneStatus);
305 Optional<Thing> theThing = getChildThing(THING_TYPE_ZONE, zoneNumber);
306 theThing.map(Thing::getHandler)
307 .ifPresent(theHandler -> ((ZoneHandler) theHandler).handleStatus(zoneStatus));
308 } else if (status instanceof ExtendedAreaStatus) {
309 final SystemType systemType = this.systemType;
310 ExtendedAreaStatus areaStatus = (ExtendedAreaStatus) status;
311 int areaNumber = areaStatus.getNumber();
313 if (systemType != null) {
314 logger.debug("Received status update for Area: {}, status: {}", areaNumber, areaStatus);
315 Optional<Thing> theThing;
316 switch (systemType) {
318 theThing = getChildThing(THING_TYPE_OMNI_AREA, areaNumber);
321 theThing = getChildThing(THING_TYPE_LUMINA_AREA, areaNumber);
324 theThing = Optional.empty();
326 theThing.map(Thing::getHandler)
327 .ifPresent(theHandler -> ((AbstractAreaHandler) theHandler).handleStatus(areaStatus));
329 logger.debug("Received null System Type!");
331 } else if (status instanceof ExtendedAccessControlReaderLockStatus) {
332 ExtendedAccessControlReaderLockStatus lockStatus = (ExtendedAccessControlReaderLockStatus) status;
333 int lockNumber = lockStatus.getNumber();
335 logger.debug("Received status update for Lock: {}, status: {}", lockNumber, lockStatus);
336 Optional<Thing> theThing = getChildThing(THING_TYPE_LOCK, lockNumber);
337 theThing.map(Thing::getHandler)
338 .ifPresent(theHandler -> ((LockHandler) theHandler).handleStatus(lockStatus));
339 } else if (status instanceof ExtendedThermostatStatus) {
340 ExtendedThermostatStatus thermostatStatus = (ExtendedThermostatStatus) status;
341 int thermostatNumber = thermostatStatus.getNumber();
343 logger.debug("Received status update for Thermostat: {}, status: {}", thermostatNumber,
345 Optional<Thing> theThing = getChildThing(THING_TYPE_THERMOSTAT, thermostatNumber);
346 theThing.map(Thing::getHandler)
347 .ifPresent(theHandler -> ((ThermostatHandler) theHandler).handleStatus(thermostatStatus));
348 } else if (status instanceof ExtendedAudioZoneStatus) {
349 ExtendedAudioZoneStatus audioZoneStatus = (ExtendedAudioZoneStatus) status;
350 int audioZoneNumber = audioZoneStatus.getNumber();
352 logger.debug("Received status update for Audio Zone: {}, status: {}", audioZoneNumber,
354 Optional<Thing> theThing = getChildThing(THING_TYPE_AUDIO_ZONE, audioZoneNumber);
355 theThing.map(Thing::getHandler)
356 .ifPresent(theHandler -> ((AudioZoneHandler) theHandler).handleStatus(audioZoneStatus));
357 } else if (status instanceof ExtendedAuxSensorStatus) {
358 ExtendedAuxSensorStatus auxSensorStatus = (ExtendedAuxSensorStatus) status;
359 int auxSensorNumber = auxSensorStatus.getNumber();
361 // Aux Sensors can be either temperature or humidity, need to check both.
362 Optional<Thing> tempThing = getChildThing(THING_TYPE_TEMP_SENSOR, auxSensorNumber);
363 Optional<Thing> humidityThing = getChildThing(THING_TYPE_HUMIDITY_SENSOR, auxSensorNumber);
364 if (tempThing.isPresent()) {
365 logger.debug("Received status update for Temperature Sensor: {}, status: {}", auxSensorNumber,
367 tempThing.map(Thing::getHandler).ifPresent(
368 theHandler -> ((TempSensorHandler) theHandler).handleStatus(auxSensorStatus));
370 if (humidityThing.isPresent()) {
371 logger.debug("Received status update for Humidity Sensor: {}, status: {}", auxSensorNumber,
373 humidityThing.map(Thing::getHandler).ifPresent(
374 theHandler -> ((HumiditySensorHandler) theHandler).handleStatus(auxSensorStatus));
377 logger.debug("Received Object Status Notification that was not processed: {}", objectStatus);
381 logger.debug("Received null Object Status Notification!");
386 public void systemEventNotification(@Nullable SystemEvent event) {
388 logger.debug("Received System Event Notification of type: {}", event.getType());
389 switch (event.getType()) {
390 case PHONE_LINE_DEAD:
391 case PHONE_LINE_OFF_HOOK:
392 case PHONE_LINE_ON_HOOK:
393 case PHONE_LINE_RING:
394 ChannelUID channel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_PHONE_LINE_EVENT);
395 triggerChannel(channel, event.getType().toString().replaceAll("^PHONE_LINE_", ""));
398 case AC_POWER_RESTORED:
399 ChannelUID acChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_AC_POWER_EVENT);
400 triggerChannel(acChannel, event.getType().toString().replaceAll("^AC_POWER_", ""));
404 ChannelUID batteryChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_BATTERY_EVENT);
405 triggerChannel(batteryChannel, event.getType().toString().replaceAll("^BATTERY_", ""));
409 ChannelUID dcmChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_DCM_EVENT);
410 triggerChannel(dcmChannel, event.getType().toString().replaceAll("^DCM_", ""));
412 case ENERGY_COST_CRITICAL:
413 case ENERGY_COST_HIGH:
414 case ENERGY_COST_LOW:
415 case ENERGY_COST_MID:
416 ChannelUID energyChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_ENERGY_COST_EVENT);
417 triggerChannel(energyChannel, event.getType().toString().replaceAll("^ENERGY_COST_", ""));
419 case CAMERA_1_TRIGGER:
420 case CAMERA_2_TRIGGER:
421 case CAMERA_3_TRIGGER:
422 case CAMERA_4_TRIGGER:
423 case CAMERA_5_TRIGGER:
424 case CAMERA_6_TRIGGER:
425 ChannelUID cameraChannel = new ChannelUID(getThing().getUID(),
426 TRIGGER_CHANNEL_CAMERA_TRIGGER_EVENT);
427 triggerChannel(cameraChannel, String.valueOf(event.getType().toString().charAt(8)));
430 Optional<Thing> buttonThing = getChildThing(THING_TYPE_BUTTON,
431 ((ButtonEvent) event).getButtonNumber());
432 buttonThing.map(Thing::getHandler)
433 .ifPresent(theHandler -> ((ButtonHandler) theHandler).buttonActivated());
436 Optional<Thing> areaThing = getChildThing(THING_TYPE_OMNI_AREA, ((AllOnOffEvent) event).getArea());
437 if (areaThing.isPresent()) {
438 logger.debug("Thing for allOnOff event: {}", areaThing.get().getUID());
439 areaThing.map(Thing::getHandler).ifPresent(theHandler -> ((AbstractAreaHandler) theHandler)
440 .handleAllOnOffEvent((AllOnOffEvent) event));
444 UPBLinkEvent linkEvent = (UPBLinkEvent) event;
445 UPBLinkEvent.Command command = linkEvent.getLinkCommand();
446 int link = linkEvent.getLinkNumber();
447 handleUPBLink(link, command);
449 case ALC_UPB_RADIORA_STARLITE_SWITCH_PRESS:
450 SwitchPressEvent switchPressEvent = (SwitchPressEvent) event;
451 int unitNumber = switchPressEvent.getUnitNumber();
453 Optional<Thing> unitThing = getUnitThing(unitNumber);
454 unitThing.map(Thing::getHandler).ifPresent(
455 theHandler -> ((UnitHandler) theHandler).handleSwitchPressEvent(switchPressEvent));
458 logger.warn("Ignoring System Event Notification of type: {}", event.getType());
461 logger.debug("Received null System Event Notification!");
465 private void handleUPBLink(int link, UPBLinkEvent.Command command) {
466 final ChannelUID activateChannel;
468 if (command == UPBLinkEvent.Command.ACTIVATED) {
469 activateChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_UPB_LINK_ACTIVATED_EVENT);
470 } else if (command == UPBLinkEvent.Command.DEACTIVATED) {
471 activateChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_UPB_LINK_DEACTIVATED_EVENT);
473 logger.debug("Received unsupported UPB link event: {}", command);
476 triggerChannel(activateChannel, Integer.toString(link));
480 public void notConnectedEvent(@Nullable Exception e) {
482 logger.debug("Received an OmniLink Controller not connected event: {}", e.getMessage());
483 setOfflineAndReconnect(e.getMessage());
487 private void getSystemStatus() throws IOException, OmniNotConnectedException, OmniInvalidResponseException,
488 OmniUnknownMessageTypeException {
489 SystemStatus status = getOmniConnection().reqSystemStatus();
490 logger.debug("Received system status: {}", status);
491 // Let's update system time
492 String dateString = new StringBuilder().append(2000 + status.getYear()).append("-")
493 .append(String.format("%02d", status.getMonth())).append("-")
494 .append(String.format("%02d", status.getDay())).append("T")
495 .append(String.format("%02d", status.getHour())).append(":")
496 .append(String.format("%02d", status.getMinute())).append(":")
497 .append(String.format("%02d", status.getSecond())).toString();
498 updateState(CHANNEL_SYSTEMDATE, new DateTimeType(dateString));
501 public Message reqObjectProperties(int objectType, int objectNum, int direction, int filter1, int filter2,
502 int filter3) throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
504 return getOmniConnection().reqObjectProperties(objectType, objectNum, direction, filter1, filter2, filter3);
505 } catch (OmniNotConnectedException | IOException e) {
506 setOfflineAndReconnect(e.getMessage());
507 throw new BridgeOfflineException(e);
511 public Message requestAudioSourceStatus(final int source, final int position)
512 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
514 return getOmniConnection().reqAudioSourceStatus(source, position);
515 } catch (OmniNotConnectedException | IOException e) {
516 setOfflineAndReconnect(e.getMessage());
517 throw new BridgeOfflineException(e);
521 public ObjectStatus requestObjectStatus(final int objType, final int startObject, final int endObject,
523 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
525 return getOmniConnection().reqObjectStatus(objType, startObject, endObject, extended);
526 } catch (OmniNotConnectedException | IOException e) {
527 setOfflineAndReconnect(e.getMessage());
528 throw new BridgeOfflineException(e);
532 public Optional<TemperatureFormat> getTemperatureFormat() {
534 return Optional.of(TemperatureFormat.valueOf(reqSystemFormats().getTempFormat()));
535 } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
536 logger.debug("Could not request temperature format from controller: {}", e.getMessage());
537 return Optional.empty();
541 public void updateChannels() {
544 updateState(CHANNEL_CONSOLE_ENABLE_DISABLE_BEEPER, UnDefType.UNDEF);
545 updateState(CHANNEL_CONSOLE_BEEP, UnDefType.UNDEF);
546 } catch (IOException | OmniNotConnectedException | OmniInvalidResponseException
547 | OmniUnknownMessageTypeException e) {
548 logger.warn("Unable to update bridge channels: {}", e.getMessage());
553 public void dispose() {
554 cancelReconnectJob(true);
555 cancelEventPolling();
556 final Connection connection = omniConnection;
557 if (connection != null) {
558 connection.removeDisconnectListener(this);
559 connection.disconnect();
563 private Optional<Thing> getChildThing(ThingTypeUID type, int number) {
564 Bridge bridge = getThing();
565 return bridge.getThings().stream().filter(t -> t.getThingTypeUID().equals(type))
566 .filter(t -> ((Number) t.getConfiguration().get(THING_PROPERTIES_NUMBER)).intValue() == number)
570 private Optional<Thing> getUnitThing(int unitId) {
571 return SUPPORTED_UNIT_TYPES_UIDS.stream().map(uid -> getChildThing(uid, unitId)).flatMap(Optional::stream)
575 public Optional<AudioPlayer> getAudioPlayer() {
579 public Message readEventRecord(int eventNumber, int direction)
580 throws OmniInvalidResponseException, OmniUnknownMessageTypeException, BridgeOfflineException {
582 return getOmniConnection().readEventRecord(eventNumber, direction);
583 } catch (OmniNotConnectedException | IOException e) {
584 setOfflineAndReconnect(e.getMessage());
585 throw new BridgeOfflineException(e);
589 private void updateBridgeProperties() {
591 SystemInformation systemInformation = reqSystemInformation();
592 Map<String, String> properties = editProperties();
593 properties.put(THING_PROPERTIES_MODEL_NUMBER, Integer.toString(systemInformation.getModel()));
594 properties.put(THING_PROPERTIES_MAJOR_VERSION, Integer.toString(systemInformation.getMajor()));
595 properties.put(THING_PROPERTIES_MINOR_VERSION, Integer.toString(systemInformation.getMinor()));
596 properties.put(THING_PROPERTIES_REVISION, Integer.toString(systemInformation.getRevision()));
597 properties.put(THING_PROPERTIES_PHONE_NUMBER, systemInformation.getPhone());
598 updateProperties(properties);
599 } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
600 logger.debug("Could not request system information from OmniLink Controller: {}", e.getMessage());
605 public void initialize() {
606 scheduleReconnectJob();
609 private void scheduleReconnectJob() {
610 ScheduledFuture<?> currentReconnectJob = connectJob;
611 if (currentReconnectJob == null || currentReconnectJob.isDone()) {
612 connectJob = super.scheduler.scheduleWithFixedDelay(this::makeOmnilinkConnection, 0, autoReconnectPeriod,
617 private void cancelReconnectJob(boolean kill) {
618 ScheduledFuture<?> currentReconnectJob = connectJob;
619 if (currentReconnectJob != null) {
620 currentReconnectJob.cancel(kill);
624 private void setOfflineAndReconnect(@Nullable String message) {
625 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message);
626 cancelEventPolling();
627 final Connection connection = omniConnection;
628 if (connection != null) {
629 connection.removeDisconnectListener(this);
631 scheduleReconnectJob();
634 private void startEventPolling(int interval) {
635 ScheduledFuture<?> eventPollingJobFuture = eventPollingJob;
636 if (eventPollingJobFuture == null || eventPollingJobFuture.isDone()) {
638 eventPollingJob = super.scheduler.scheduleWithFixedDelay(this::pollEvents, 0, interval, TimeUnit.SECONDS);
642 private void cancelEventPolling() {
643 ScheduledFuture<?> eventPollingJobFuture = eventPollingJob;
644 if (eventPollingJobFuture != null) {
645 eventPollingJobFuture.cancel(true);
649 private void pollEvents() {
650 // On first run, direction is -1 (most recent event), after its 1 for the next log message
654 logger.trace("Polling for event log messages.");
655 int direction = eventLogNumber == 0 ? -1 : 1;
656 message = readEventRecord(eventLogNumber, direction);
657 if (message.getMessageType() == Message.MESG_TYPE_EVENT_LOG_DATA) {
658 EventLogData logData = (EventLogData) message;
659 logger.debug("Processing event log message number: {}", logData.getEventNumber());
660 eventLogNumber = logData.getEventNumber();
661 String json = gson.toJson(logData);
662 logger.debug("Receieved event log message: {}", json);
663 updateState(CHANNEL_EVENT_LOG, new StringType(json));
665 } while (message.getMessageType() != Message.MESG_TYPE_END_OF_DATA);
667 } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
668 logger.debug("Exception recieved while polling for event log messages: {}", e.getMessage());
672 private Connection getOmniConnection() throws OmniNotConnectedException {
673 final Connection connection = omniConnection;
674 if (connection != null) {
677 throw new OmniNotConnectedException("Connection not yet established!");