gt:         if (volume.equals("ON")) {
context: /**
   * Copyright (c) 2010-2020 Contributors to the openHAB project
   *
   * See the NOTICE file(s) distributed with this work for additional
   * information.
   *
   * This program and the accompanying materials are made available under the
   * terms of the Eclipse Public License 2.0 which is available at
   * http://www.eclipse.org/legal/epl-2.0
   *
   * SPDX-License-Identifier: EPL-2.0
   */
  package org.openhab.binding.xbmc.rpc;
  
  import java.io.IOException;
  import java.math.BigDecimal;
  import java.net.ConnectException;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.concurrent.ExecutionException;
  import java.util.concurrent.TimeoutException;
  
  import org.apache.commons.lang.StringUtils;
  import org.codehaus.jackson.JsonParseException;
  import org.codehaus.jackson.map.JsonMappingException;
  import org.codehaus.jackson.map.ObjectMapper;
  import org.openhab.binding.xbmc.internal.XbmcHost;
  import org.openhab.binding.xbmc.rpc.calls.ApplicationGetProperties;
  import org.openhab.binding.xbmc.rpc.calls.ApplicationSetVolume;
  import org.openhab.binding.xbmc.rpc.calls.FilesPrepareDownload;
  import org.openhab.binding.xbmc.rpc.calls.GUIShowNotification;
  import org.openhab.binding.xbmc.rpc.calls.InputExecuteAction;
  import org.openhab.binding.xbmc.rpc.calls.JSONRPCPing;
  import org.openhab.binding.xbmc.rpc.calls.PVRGetChannels;
  import org.openhab.binding.xbmc.rpc.calls.PlayerGetActivePlayers;
  import org.openhab.binding.xbmc.rpc.calls.PlayerGetItem;
  import org.openhab.binding.xbmc.rpc.calls.PlayerGetLabels;
  import org.openhab.binding.xbmc.rpc.calls.PlayerGetProperties;
  import org.openhab.binding.xbmc.rpc.calls.PlayerOpen;
  import org.openhab.binding.xbmc.rpc.calls.PlayerPlayPause;
  import org.openhab.binding.xbmc.rpc.calls.PlayerStop;
  import org.openhab.binding.xbmc.rpc.calls.SystemHibernate;
  import org.openhab.binding.xbmc.rpc.calls.SystemReboot;
  import org.openhab.binding.xbmc.rpc.calls.SystemShutdown;
  import org.openhab.binding.xbmc.rpc.calls.SystemSuspend;
  import org.openhab.binding.xbmc.rpc.calls.XBMCGetInfoBooleans;
  import org.openhab.core.events.EventPublisher;
  import org.openhab.core.library.types.OnOffType;
  import org.openhab.core.library.types.PercentType;
  import org.openhab.core.library.types.StringType;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import com.ning.http.client.AsyncHttpClient;
  import com.ning.http.client.AsyncHttpClientConfig;
  import com.ning.http.client.AsyncHttpClientConfig.Builder;
  import com.ning.http.client.Realm;
  import com.ning.http.client.Realm.AuthScheme;
  import com.ning.http.client.providers.netty.NettyAsyncHttpProvider;
  import com.ning.http.client.websocket.WebSocket;
  import com.ning.http.client.websocket.WebSocketTextListener;
  import com.ning.http.client.websocket.WebSocketUpgradeHandler;
  
  /**
   * Manages the web socket connection for a single XBMC instance.
   *
   * @author tlan, Ben Jones, Ard van der Leeuw, Plebs
   * @since 1.5.0
   */
  public class XbmcConnector {
  
      private static final String SCREENSAVER_STATE = "Screensaver.State";
  
      private static final Logger logger = LoggerFactory.getLogger(XbmcConnector.class);
  
      // request timeout (configurable?)
      private static final int REQUEST_TIMEOUT_MS = 60000;
  
      // the amount to increase/decrease the volume when receiving INCREASE/DECREASE commands
      private static final BigDecimal VOLUMESTEP = new BigDecimal(10);
  
      // the XBMC instance and openHAB event publisher handles
      private final XbmcHost xbmc;
      private final EventPublisher eventPublisher;
  
      private final String httpUri;
      private final String wsUri;
      private final AsyncHttpClient client;
      private final WebSocketUpgradeHandler handler;
  
      // stores which property is associated with each item
      private final Map<String, String> watches = new HashMap<String, String>();
  
      // the async connection to the XBMC instance
      private WebSocket webSocket;
      private boolean connected = false;
  
      // the current volume
      private BigDecimal volume = BigDecimal.ZERO;
  
      // the current player state
      private State currentState = State.Stop;
  
      private enum State {
          Play,
          Pause,
          End,
          Stop
      }
  
      /**
       * @param xbmc
       *            The host to connect to. Give a reachable hostname or ip
       *            address, without protocol or port
       * @param eventPublisher
       *            EventPublisher to push out state updates
       */
      public XbmcConnector(XbmcHost xbmc, EventPublisher eventPublisher) {
          this.xbmc = xbmc;
          this.eventPublisher = eventPublisher;
  
          this.httpUri = String.format("http://%s:%d/jsonrpc", xbmc.getHostname(), xbmc.getPort());
          this.wsUri = String.format("ws://%s:%d/jsonrpc", xbmc.getHostname(), xbmc.getWSPort());
  
          this.client = new AsyncHttpClient(new NettyAsyncHttpProvider(createAsyncHttpClientConfig()));
          this.handler = createWebSocketHandler();
      }
  
      /***
       * Check if the connection to the XBMC instance is active
       *
       * @return true if an active connection to the XBMC instance exists, false otherwise
       */
      public boolean isConnected() {
          if (webSocket == null || !webSocket.isOpen()) {
              return false;
          }
  
          return connected;
      }
  
      /**
       * Attempts to create a connection to the XBMC host and begin listening
       * for updates over the async http web socket
       *
       * @throws ExecutionException
       * @throws InterruptedException
       * @throws IOException
       */
      public void open() throws IOException, InterruptedException, ExecutionException {
          // cleanup any existing web socket left over from previous attempts
          close();
  
          // attempt to open the web socket connection to the XBMC instance
          webSocket = client.prepareGet(wsUri).execute(handler).get();
      }
  
      /***
       * Close this connection to the XBMC instance
       */
      public void close() {
          // if there is an old web socket then clean up and destroy
          if (webSocket != null) {
              webSocket.close();
              webSocket = null;
          }
      }
  
      private AsyncHttpClientConfig createAsyncHttpClientConfig() {
          Builder builder = new AsyncHttpClientConfig.Builder();
          builder.setRealm(createRealm());
          builder.setRequestTimeoutInMs(REQUEST_TIMEOUT_MS);
          return builder.build();
      }
  
      private Realm createRealm() {
          Realm.RealmBuilder builder = new Realm.RealmBuilder();
          builder.setPrincipal(xbmc.getUsername());
          builder.setPassword(xbmc.getPassword());
          builder.setUsePreemptiveAuth(true);
          builder.setScheme(AuthScheme.BASIC);
          return builder.build();
      }
  
      private WebSocketUpgradeHandler createWebSocketHandler() {
          WebSocketUpgradeHandler.Builder builder = new WebSocketUpgradeHandler.Builder();
          builder.addWebSocketListener(new XbmcWebSocketListener());
          return builder.build();
      }
  
      class XbmcWebSocketListener implements WebSocketTextListener {
          private final ObjectMapper mapper = new ObjectMapper();
  
          @Override
          public void onOpen(WebSocket webSocket) {
              logger.debug("[{}]: Websocket opened", xbmc.getHostname());
              connected = true;
              requestApplicationUpdate();
              requestScreenSaverStateUpdate();
              updatePlayerStatus();
              updateProperty("System.State", OnOffType.ON);
          }
  
          @Override
          public void onError(Throwable e) {
              if (e instanceof ConnectException) {
                  logger.debug("[{}]: Websocket connection error '{}'", xbmc.getHostname(), e.getMessage());
              } else if (e instanceof TimeoutException) {
                  logger.debug("[{}]: Websocket timeout error", xbmc.getHostname());
              } else {
                  logger.error("[{}]: Websocket error: {}", xbmc.getHostname(), e.getMessage());
              }
          }
  
          @Override
          public void onClose(WebSocket webSocket) {
              logger.warn("[{}]: Websocket closed", xbmc.getHostname());
              webSocket = null;
              connected = false;
              updateProperty("System.State", OnOffType.OFF);
          }
  
          @Override
          @SuppressWarnings("unchecked")
          public void onMessage(String message) {
              logger.debug("[{}]: Message received: {}", xbmc.getHostname(), message);
              Map<String, Object> json;
              try {
                  json = mapper.readValue(message, Map.class);
              } catch (JsonParseException e) {
                  logger.error("Error parsing JSON", e);
                  return;
              } catch (JsonMappingException e) {
                  logger.error("Error mapping JSON", e);
                  return;
              } catch (IOException e) {
                  logger.error("An I/O error occurred while decoding JSON", e);
                  return;
              }
  
              // We only care about certain notifications on the websocket
              // feed, since all our actual data fetching is done via http
              try {
                  if (json.containsKey("method")) {
                      String method = (String) json.get("method");
                      if (method.startsWith("Player.On")) {
                          processPlayerStateChanged(method, json);
                      } else if (method.startsWith("Application.On")) {
                          processApplicationStateChanged(method, json);
                      } else if (method.startsWith("System.On")) {
                          processSystemStateChanged(method, json);
                      } else if (method.startsWith("GUI.OnScreensaver")) {
                          processScreensaverStateChanged(method, json);
                      }
                  }
              } catch (Exception e) {
                  logger.error("Error handling player state change message", e);
              }
          }
  
          @Override
          public void onFragment(String fragment, boolean last) {
          }
      }
  
      /**
       * Send a ping to the XBMC host and wait for a 'pong'.
       */
      public void ping() {
          final JSONRPCPing ping = new JSONRPCPing(client, httpUri);
  
          ping.execute(new Runnable() {
              @Override
              public void run() {
                  connected = ping.isPong();
              }
          });
      }
  
      /**
       * Create a mapping between an item and an xbmc property
       *
       * @param itemName
       *            The name of the item which should receive updates
       * @param property
       *            The property of this xbmc instance, which is to be
       *            watched for changes
       *
       */
      public void addItem(String itemName, String property) {
          logger.debug("Mapping: itemname = {} & property = {}", itemName, property);
          if (!watches.containsKey(itemName)) {
              watches.put(itemName, property);
          }
      }
  
      public void updateSystemStatus() {
          if (connected) {
              updateProperty("System.State", OnOffType.ON);
          } else {
              updateProperty("System.State", OnOffType.OFF);
          }
      }
  
      /**
       * Update the status of the current player
       */
      public void updatePlayerStatus() {
          updatePlayerStatus(false);
      }
  
      /**
       * Update the status of the current player
       *
       * @param updatePolledPropertiesOnly
       *            If updatePolledPropertiesOnly is true, only update the Player properties that need to be polled
       *            If updatePolledPropertiesOnly is false, update the Player state itself as well
       */
      public void updatePlayerStatus(final boolean updatePolledPropertiesOnly) {
          final PlayerGetActivePlayers activePlayers = new PlayerGetActivePlayers(client, httpUri);
  
          activePlayers.execute(new Runnable() {
              @Override
              public void run() {
                  if (activePlayers.isPlaying()) {
                      if (!updatePolledPropertiesOnly) {
                          updateState(State.Play);
                      }
                      requestPlayerUpdate(activePlayers.getPlayerId(), updatePolledPropertiesOnly);
                  } else {
                      if (!updatePolledPropertiesOnly) {
                          updateState(State.Stop);
                      }
                  }
              }
          });
      }
  
      private void updateScreenSaverStatus(boolean screenSaverActive) {
          if (screenSaverActive) {
              updateProperty(SCREENSAVER_STATE, OnOffType.ON);
          } else {
              updateProperty(SCREENSAVER_STATE, OnOffType.OFF);
          }
      }
  
      public void playerPlayPause() {
          final PlayerGetActivePlayers activePlayers = new PlayerGetActivePlayers(client, httpUri);
  
          activePlayers.execute(new Runnable() {
              @Override
              public void run() {
                  PlayerPlayPause playPause = new PlayerPlayPause(client, httpUri);
                  playPause.setPlayerId(activePlayers.getPlayerId());
                  playPause.execute();
              }
          });
      }
  
      public void playerStop() {
          final PlayerGetActivePlayers activePlayers = new PlayerGetActivePlayers(client, httpUri);
  
          activePlayers.execute(new Runnable() {
              @Override
              public void run() {
                  PlayerStop stop = new PlayerStop(client, httpUri);
                  stop.setPlayerId(activePlayers.getPlayerId());
                  stop.execute();
              }
          });
      }
  
      public void showNotification(String title, String message) {
          final GUIShowNotification showNotification = new GUIShowNotification(client, httpUri);
  
          showNotification.setTitle(title);
          showNotification.setMessage(message);
          showNotification.execute();
      }
  
      public void systemShutdown() {
          final SystemShutdown shutdown = new SystemShutdown(client, httpUri);
          shutdown.execute();
      }
  
      public void systemSuspend() {
          final SystemSuspend suspend = new SystemSuspend(client, httpUri);
          suspend.execute();
      }
  
      public void systemHibernate() {
          final SystemHibernate hibernate = new SystemHibernate(client, httpUri);
          hibernate.execute();
      }
  
      public void systemReboot() {
          final SystemReboot reboot = new SystemReboot(client, httpUri);
          reboot.execute();
      }
  
      public void playerOpen(String file) {
          final PlayerOpen playeropen = new PlayerOpen(client, httpUri);
          playeropen.setFile(file);
          playeropen.execute();
      }
  
      public void inputExecuteAction(String action) {
          final InputExecuteAction executeAction = new InputExecuteAction(client, httpUri);
          executeAction.setAction(action);
          executeAction.execute();
      }
  
      public void applicationSetVolume(String volume) {
          final ApplicationSetVolume applicationsetvolume = new ApplicationSetVolume(client, httpUri);
  
  