2 * Copyright (c) 2010-2024 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.androidtv.internal.protocol.philipstv.service;
15 import static org.openhab.binding.androidtv.internal.AndroidTVBindingConstants.*;
16 import static org.openhab.binding.androidtv.internal.protocol.philipstv.ConnectionManager.OBJECT_MAPPER;
17 import static org.openhab.binding.androidtv.internal.protocol.philipstv.PhilipsTVBindingConstants.*;
19 import java.io.IOException;
21 import java.util.concurrent.ConcurrentMap;
22 import java.util.function.Function;
23 import java.util.stream.Collectors;
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.binding.androidtv.internal.protocol.philipstv.ConnectionManager;
28 import org.openhab.binding.androidtv.internal.protocol.philipstv.PhilipsTVConnectionManager;
29 import org.openhab.binding.androidtv.internal.protocol.philipstv.service.api.PhilipsTVService;
30 import org.openhab.binding.androidtv.internal.protocol.philipstv.service.model.channel.AvailableTvChannelsDTO;
31 import org.openhab.binding.androidtv.internal.protocol.philipstv.service.model.channel.ChannelDTO;
32 import org.openhab.binding.androidtv.internal.protocol.philipstv.service.model.channel.ChannelListDTO;
33 import org.openhab.binding.androidtv.internal.protocol.philipstv.service.model.channel.TvChannelDTO;
34 import org.openhab.core.library.types.StringType;
35 import org.openhab.core.thing.ThingStatus;
36 import org.openhab.core.thing.ThingStatusDetail;
37 import org.openhab.core.types.Command;
38 import org.openhab.core.types.RefreshType;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
43 * Service for handling commands regarding setting or retrieving the TV channel
45 * @author Benjamin Meyer - Initial contribution
46 * @author Ben Rosenblum - Merged into AndroidTV
49 public class TvChannelService implements PhilipsTVService {
51 private final Logger logger = LoggerFactory.getLogger(getClass());
53 // Name , ccid of TV Channel
54 private @Nullable Map<String, String> availableTvChannels;
56 private final PhilipsTVConnectionManager handler;
58 private final ConnectionManager connectionManager;
60 public TvChannelService(PhilipsTVConnectionManager handler, ConnectionManager connectionManager) {
61 this.handler = handler;
62 this.connectionManager = connectionManager;
66 public void handleCommand(String channel, Command command) {
69 if (isTvChannelListEmpty()) {
70 availableTvChannels = getAvailableTvChannelListFromTv();
71 handler.updateChannelStateDescription(CHANNEL_TV_CHANNEL, availableTvChannels.keySet().stream()
72 .collect(Collectors.toMap(Function.identity(), Function.identity())));
75 if (command instanceof RefreshType) {
76 // Get current tv channel name
77 String tvChannelName = getCurrentTvChannel();
78 handler.postUpdateChannel(CHANNEL_TV_CHANNEL, new StringType(tvChannelName));
79 } else if (command instanceof StringType) {
80 if (availableTvChannels.containsKey(command.toString())) {
81 switchTvChannel(command);
84 "The given TV Channel with Name: {} couldn't be found in the local Channel List from the TV.",
88 logger.warn("Unknown command: {} for Channel {}", command, channel);
90 } catch (Exception e) {
91 if (isTvOfflineException(e)) {
92 logger.warn("Could not execute command for TV Channels, the TV is offline.");
93 handler.postUpdateThing(ThingStatus.OFFLINE, ThingStatusDetail.NONE, TV_OFFLINE_MSG);
94 } else if (isTvNotListeningException(e)) {
95 handler.postUpdateThing(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
96 TV_NOT_LISTENING_MSG);
98 logger.warn("Error occurred during handling of command for TV Channels: {}", e.getMessage(), e);
103 private boolean isTvChannelListEmpty() {
104 return (availableTvChannels == null) || availableTvChannels.isEmpty();
107 private Map<String, String> getAvailableTvChannelListFromTv() throws IOException {
108 AvailableTvChannelsDTO availableTvChannelsDTO = OBJECT_MAPPER.readValue(
109 connectionManager.doHttpsGet(GET_AVAILABLE_TV_CHANNEL_LIST_PATH), AvailableTvChannelsDTO.class);
111 ConcurrentMap<String, String> tvChannelsMap = availableTvChannelsDTO.getChannel().stream()
112 .collect(Collectors.toConcurrentMap(ChannelDTO::getName, ChannelDTO::getCcid, (c1, c2) -> c1));
114 logger.debug("TV Channels added: {}", tvChannelsMap.size());
115 if (logger.isTraceEnabled()) {
116 tvChannelsMap.keySet().forEach(app -> logger.trace("TV Channel found: {}", app));
118 return tvChannelsMap;
121 private String getCurrentTvChannel() throws IOException {
122 TvChannelDTO tvChannelDTO = OBJECT_MAPPER.readValue(connectionManager.doHttpsGet(TV_CHANNEL_PATH),
124 ChannelDTO channel = tvChannelDTO.getChannel();
125 return channel != null ? channel.getName() : "NA";
128 private void switchTvChannel(Command command) throws IOException {
129 ChannelDTO channelDTO = new ChannelDTO();
130 channelDTO.setCcid(availableTvChannels.get(command.toString()));
132 ChannelListDTO channelListDTO = new ChannelListDTO();
133 channelListDTO.setId("allter");
134 channelListDTO.setVersion("30");
136 TvChannelDTO tvChannelDTO = new TvChannelDTO(channelDTO, channelListDTO);
137 String switchTvChannelJson = OBJECT_MAPPER.writeValueAsString(tvChannelDTO);
138 logger.debug("Switch TV Channel json: {}", switchTvChannelJson);
139 connectionManager.doHttpsPost(TV_CHANNEL_PATH, switchTvChannelJson);
142 public void clearAvailableTvChannelList() {
143 if (availableTvChannels != null) {
144 availableTvChannels.clear();