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.Optional;
22 import java.util.concurrent.ConcurrentMap;
23 import java.util.function.Function;
24 import java.util.stream.Collectors;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.binding.androidtv.internal.protocol.philipstv.ConnectionManager;
29 import org.openhab.binding.androidtv.internal.protocol.philipstv.PhilipsTVConnectionManager;
30 import org.openhab.binding.androidtv.internal.protocol.philipstv.service.api.PhilipsTVService;
31 import org.openhab.binding.androidtv.internal.protocol.philipstv.service.model.channel.AvailableTvChannelsDTO;
32 import org.openhab.binding.androidtv.internal.protocol.philipstv.service.model.channel.ChannelDTO;
33 import org.openhab.binding.androidtv.internal.protocol.philipstv.service.model.channel.ChannelListDTO;
34 import org.openhab.binding.androidtv.internal.protocol.philipstv.service.model.channel.TvChannelDTO;
35 import org.openhab.core.library.types.StringType;
36 import org.openhab.core.thing.ThingStatus;
37 import org.openhab.core.thing.ThingStatusDetail;
38 import org.openhab.core.types.Command;
39 import org.openhab.core.types.RefreshType;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
44 * Service for handling commands regarding setting or retrieving the TV channel
46 * @author Benjamin Meyer - Initial contribution
47 * @author Ben Rosenblum - Merged into AndroidTV
50 public class TvChannelService implements PhilipsTVService {
52 private final Logger logger = LoggerFactory.getLogger(getClass());
54 // Name , ccid of TV Channel
55 private @Nullable Map<String, String> availableTvChannels;
57 private final PhilipsTVConnectionManager handler;
59 private final ConnectionManager connectionManager;
61 public TvChannelService(PhilipsTVConnectionManager handler, ConnectionManager connectionManager) {
62 this.handler = handler;
63 this.connectionManager = connectionManager;
67 public void handleCommand(String channel, Command command) {
70 if (isTvChannelListEmpty()) {
71 availableTvChannels = getAvailableTvChannelListFromTv();
72 handler.updateChannelStateDescription(CHANNEL_TV_CHANNEL, availableTvChannels.keySet().stream()
73 .collect(Collectors.toMap(Function.identity(), Function.identity())));
76 if (command instanceof RefreshType) {
77 // Get current tv channel name
78 String tvChannelName = getCurrentTvChannel();
79 handler.postUpdateChannel(CHANNEL_TV_CHANNEL, new StringType(tvChannelName));
80 } else if (command instanceof StringType) {
81 if (availableTvChannels.containsKey(command.toString())) {
82 switchTvChannel(command);
85 "The given TV Channel with Name: {} couldn't be found in the local Channel List from the TV.",
89 logger.warn("Unknown command: {} for Channel {}", command, channel);
91 } catch (Exception e) {
92 if (isTvOfflineException(e)) {
93 logger.warn("Could not execute command for TV Channels, the TV is offline.");
94 handler.postUpdateThing(ThingStatus.OFFLINE, ThingStatusDetail.NONE, TV_OFFLINE_MSG);
95 } else if (isTvNotListeningException(e)) {
96 handler.postUpdateThing(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
97 TV_NOT_LISTENING_MSG);
99 logger.warn("Error occurred during handling of command for TV Channels: {}", e.getMessage(), e);
104 private boolean isTvChannelListEmpty() {
105 return (availableTvChannels == null) || availableTvChannels.isEmpty();
108 private Map<String, String> getAvailableTvChannelListFromTv() throws IOException {
109 AvailableTvChannelsDTO availableTvChannelsDTO = OBJECT_MAPPER.readValue(
110 connectionManager.doHttpsGet(GET_AVAILABLE_TV_CHANNEL_LIST_PATH), AvailableTvChannelsDTO.class);
112 ConcurrentMap<String, String> tvChannelsMap = availableTvChannelsDTO.getChannel().stream()
113 .collect(Collectors.toConcurrentMap(ChannelDTO::getName, ChannelDTO::getCcid, (c1, c2) -> c1));
115 logger.debug("TV Channels added: {}", tvChannelsMap.size());
116 if (logger.isTraceEnabled()) {
117 tvChannelsMap.keySet().forEach(app -> logger.trace("TV Channel found: {}", app));
119 return tvChannelsMap;
122 private String getCurrentTvChannel() throws IOException {
123 TvChannelDTO tvChannelDTO = OBJECT_MAPPER.readValue(connectionManager.doHttpsGet(TV_CHANNEL_PATH),
125 return Optional.ofNullable(tvChannelDTO.getChannel()).map(ChannelDTO::getName).orElse("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();