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.util.Optional;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.omnilink.internal.AudioPlayer;
22 import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest;
23 import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests;
24 import org.openhab.binding.omnilink.internal.exceptions.BridgeOfflineException;
25 import org.openhab.core.library.types.DecimalType;
26 import org.openhab.core.library.types.NextPreviousType;
27 import org.openhab.core.library.types.OnOffType;
28 import org.openhab.core.library.types.PercentType;
29 import org.openhab.core.library.types.PlayPauseType;
30 import org.openhab.core.thing.ChannelUID;
31 import org.openhab.core.thing.Thing;
32 import org.openhab.core.thing.ThingStatus;
33 import org.openhab.core.thing.ThingStatusDetail;
34 import org.openhab.core.types.Command;
35 import org.openhab.core.types.RefreshType;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
39 import com.digitaldan.jomnilinkII.Message;
40 import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage;
41 import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
42 import com.digitaldan.jomnilinkII.MessageTypes.properties.AudioZoneProperties;
43 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedAudioZoneStatus;
44 import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
45 import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
48 * The {@link AudioZoneHandler} defines some methods that are used to
49 * interface with an OmniLink Audio Zone. This by extension also defines the
50 * Audio Zone thing that openHAB will be able to pick up and interface with.
52 * @author Craig Hamilton - Initial contribution
53 * @author Ethan Dye - openHAB3 rewrite
56 public class AudioZoneHandler extends AbstractOmnilinkStatusHandler<ExtendedAudioZoneStatus> {
57 private final Logger logger = LoggerFactory.getLogger(AudioZoneHandler.class);
58 private final int thingID = getThingNumber();
59 public @Nullable String number;
61 public AudioZoneHandler(Thing thing) {
66 public void initialize() {
68 final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
69 if (bridgeHandler != null) {
70 updateAudioZoneProperties(bridgeHandler);
72 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
73 "Received null bridge while initializing Audio Zone!");
77 private void updateAudioZoneProperties(OmnilinkBridgeHandler bridgeHandler) {
78 ObjectPropertyRequest<AudioZoneProperties> objectPropertyRequest = ObjectPropertyRequest
79 .builder(bridgeHandler, ObjectPropertyRequests.AUDIO_ZONE, thingID, 0).selectNamed().build();
81 for (AudioZoneProperties audioZoneProperties : objectPropertyRequest) {
82 updateProperty(THING_PROPERTIES_NAME, audioZoneProperties.getName());
87 public void handleCommand(ChannelUID channelUID, Command command) {
88 logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
90 if (command instanceof RefreshType) {
91 retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
92 ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null status update!"));
96 switch (channelUID.getId()) {
97 case CHANNEL_AUDIO_ZONE_POWER:
98 if (command instanceof OnOffType) {
99 sendOmnilinkCommand(CommandMessage.CMD_AUDIO_ZONE_SET_ON_AND_MUTE,
100 OnOffType.OFF.equals(command) ? 0 : 1, thingID);
102 logger.debug("Invalid command: {}, must be OnOffType", command);
105 case CHANNEL_AUDIO_ZONE_MUTE:
106 if (command instanceof OnOffType) {
107 sendOmnilinkCommand(CommandMessage.CMD_AUDIO_ZONE_SET_ON_AND_MUTE,
108 OnOffType.OFF.equals(command) ? 2 : 3, thingID);
110 logger.debug("Invalid command: {}, must be OnOffType", command);
113 case CHANNEL_AUDIO_ZONE_VOLUME:
114 if (command instanceof PercentType) {
115 sendOmnilinkCommand(CommandMessage.CMD_AUDIO_ZONE_SET_VOLUME, ((PercentType) command).intValue(),
118 logger.debug("Invalid command: {}, must be PercentType", command);
121 case CHANNEL_AUDIO_ZONE_SOURCE:
122 if (command instanceof DecimalType) {
123 sendOmnilinkCommand(CommandMessage.CMD_AUDIO_ZONE_SET_SOURCE, ((DecimalType) command).intValue(),
126 logger.debug("Invalid command: {}, must be DecimalType", command);
129 case CHANNEL_AUDIO_ZONE_CONTROL:
130 if (command instanceof PlayPauseType) {
131 handlePlayPauseCommand(channelUID, (PlayPauseType) command);
132 } else if (command instanceof NextPreviousType) {
133 handleNextPreviousCommand(channelUID, (NextPreviousType) command);
135 logger.debug("Invalid command: {}, must be PlayPauseType or NextPreviousType", command);
139 logger.warn("Unknown channel for Audio Zone thing: {}", channelUID);
143 private void handlePlayPauseCommand(ChannelUID channelUID, PlayPauseType command) {
144 logger.debug("handlePlayPauseCommand called for channel: {}, command: {}", channelUID, command);
145 final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
147 if (bridgeHandler != null) {
148 Optional<AudioPlayer> audioPlayer = bridgeHandler.getAudioPlayer();
149 if (audioPlayer.isPresent()) {
150 AudioPlayer player = audioPlayer.get();
151 sendOmnilinkCommand(CommandMessage.CMD_AUDIO_ZONE_SET_SOURCE,
152 PlayPauseType.PLAY.equals(command) ? player.getPlayCommand() : player.getPauseCommand(),
155 logger.warn("No Audio Player was detected!");
158 logger.debug("Received null bridge while sending Audio Zone command!");
162 private void handleNextPreviousCommand(ChannelUID channelUID, NextPreviousType command) {
163 logger.debug("handleNextPreviousCommand called for channel: {}, command: {}", channelUID, command);
164 final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
166 if (bridgeHandler != null) {
167 Optional<AudioPlayer> audioPlayer = bridgeHandler.getAudioPlayer();
168 if (audioPlayer.isPresent()) {
169 AudioPlayer player = audioPlayer.get();
170 sendOmnilinkCommand(CommandMessage.CMD_AUDIO_ZONE_SET_SOURCE,
171 NextPreviousType.NEXT.equals(command) ? player.getNextCommand() : player.getPreviousCommand(),
174 logger.warn("Audio Player could not be found!");
177 logger.debug("Received null bridge while sending Audio Zone command!");
182 public void updateChannels(ExtendedAudioZoneStatus status) {
183 logger.debug("updateChannels called for Audio Zone status: {}", status);
184 updateState(CHANNEL_AUDIO_ZONE_POWER, OnOffType.from(status.isPower()));
185 updateState(CHANNEL_AUDIO_ZONE_MUTE, OnOffType.from(status.isMute()));
186 updateState(CHANNEL_AUDIO_ZONE_VOLUME, new PercentType(status.getVolume()));
187 updateState(CHANNEL_AUDIO_ZONE_SOURCE, new DecimalType(status.getSource()));
191 protected Optional<ExtendedAudioZoneStatus> retrieveStatus() {
193 final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
194 if (bridgeHandler != null) {
195 ObjectStatus objStatus = bridgeHandler.requestObjectStatus(Message.OBJ_TYPE_AUDIO_ZONE, thingID,
197 return Optional.of((ExtendedAudioZoneStatus) objStatus.getStatuses()[0]);
199 logger.debug("Received null bridge while updating Audio Zone status!");
200 return Optional.empty();
202 } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
203 logger.debug("Received exception while refreshing Audio Zone status: {}", e.getMessage());
204 return Optional.empty();