]> git.basschouten.com Git - openhab-addons.git/blob
7ef7907ecde7d02cf099a8335797235a5aaa5718
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.freeboxos.internal.api.rest;
14
15 import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.THING_PLAYER;
16
17 import java.time.ZonedDateTime;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21
22 import javax.ws.rs.core.UriBuilder;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.eclipse.jetty.http.HttpMethod;
27 import org.openhab.binding.freeboxos.internal.api.FreeboxException;
28 import org.openhab.binding.freeboxos.internal.api.Response;
29 import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.Metadata.PlaybackState;
30 import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.Metadata.SubtitleTrack;
31 import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.Metadata.VideoTrack;
32 import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.PlayerContext.PlayerDetails;
33 import org.openhab.binding.freeboxos.internal.api.rest.SystemManager.ModelInfo;
34
35 import com.google.gson.annotations.SerializedName;
36
37 import inet.ipaddr.mac.MACAddress;
38
39 /**
40  * The {@link PlayerManager} is the Java class used to handle api requests related to player
41  *
42  * @author GaĆ«l L'hopital - Initial contribution
43  */
44 @NonNullByDefault
45 public class PlayerManager extends ListableRest<PlayerManager.Player, PlayerManager.PlayerResponse> {
46     private static final String STATUS_PATH = "status";
47
48     protected static class PlayerResponse extends Response<Player> {
49     }
50
51     public static enum DeviceModel {
52         FBX7HD_DELTA, // Freebox Player Devialet
53         TBX8AM, // Player Pop
54         FBX6HD,
55         FBX6LC,
56         FBX6LCV2,
57         FBX7HD,
58         FBX7HD_ONE,
59         FBX8AM,
60         UNKNOWN;
61     }
62
63     public static record Player(MACAddress mac, StbType stbType, int id, ZonedDateTime lastTimeReachable,
64             boolean apiAvailable, String deviceName, DeviceModel deviceModel, boolean reachable, String uid,
65             @Nullable String apiVersion, List<String> lanGids) {
66         private static enum StbType {
67             STB_ANDROID,
68             STB_V6,
69             STB_V7,
70             STB_V8,
71             UNKNOWN;
72         }
73
74         /**
75          * @return a string like eg: '17/api/v8'
76          */
77         private @Nullable String baseUrl() {
78             String api = apiVersion;
79             return api != null ? "%d/api/v%s/".formatted(id, api.split("\\.")[0]) : null;
80         }
81     }
82
83     private static class StatusResponse extends Response<Status> {
84     }
85
86     public static enum PowerState {
87         STANDBY,
88         RUNNING,
89         UNKNOWN;
90     }
91
92     public static record Status(PowerState powerState, StatusInformation player,
93             @Nullable ForegroundApp foregroundApp) {
94
95         public @Nullable ForegroundApp foregroundApp() {
96             return foregroundApp;
97         }
98     }
99
100     public static record ForegroundApp(int packageId, @Nullable String curlUrl, @Nullable Object context,
101             @SerializedName(value = "package") String _package) {
102     }
103
104     private static record StatusInformation(String name, ZonedDateTime lastActivity) {
105     }
106
107     private static class ConfigurationResponse extends Response<Configuration> {
108     }
109
110     public static record Configuration(String boardName, boolean configured, String firmwareVersion,
111             @Nullable ModelInfo modelInfo, String serial, String uptime, long uptimeVal) {
112     }
113
114     private enum MediaState {
115         READY,
116         UNKNOWN;
117     }
118
119     private static record AudioTrack(int bitrate, @SerializedName("channelCount") int channelCount,
120             @Nullable String codec, @SerializedName("codecId") @Nullable String codecId, @Nullable String language,
121             @SerializedName("metadataId") @Nullable String metadataId, int pid, int samplerate, long uid) {
122     }
123
124     private static enum Type {
125         NORMAL,
126         HEARINGIMPAIRED,
127         UNKNOWN;
128     }
129
130     protected static record Metadata(@Nullable String album,
131             @SerializedName("albumArtist") @Nullable String albumArtist, @Nullable String artist,
132             @Nullable String author, int bpm, @Nullable String comment, boolean compilation, @Nullable String composer,
133             @Nullable String container, @Nullable String copyright, long date,
134             @SerializedName("discId") @Nullable String discId, @SerializedName("discNumber") int discNumber,
135             @SerializedName("discTotal") int discTotal, @Nullable String genre,
136             @SerializedName("musicbrainzDiscId") @Nullable String musicbrainzDiscId, @Nullable String performer,
137             @Nullable String title, @SerializedName("trackNumber") int trackNumber,
138             @SerializedName("trackTotal") int trackTotal, @Nullable String url) {
139
140         protected static enum PlaybackState {
141             PLAY,
142             PAUSE,
143             UNKNOWN;
144         }
145
146         protected static record SubtitleTrack(@Nullable String codec, @Nullable String language, @Nullable String pid,
147                 Type type, @Nullable String uid) {
148         }
149
150         protected static record VideoTrack(int bitrate, @Nullable String codec, int height, int pid, int uid,
151                 int width) {
152         }
153     }
154
155     public static record PlayerContext(@Nullable PlayerDetails player) {
156         public static record PlayerDetails(@SerializedName("audioIndex") int audioIndex,
157                 @SerializedName("audioList") List<AudioTrack> audioList, @SerializedName("curPos") long curPos,
158                 int duration, @SerializedName("livePos") long livePos, @SerializedName("maxPos") long maxPos,
159                 @SerializedName("mediaState") MediaState mediaState, @Nullable Metadata metadata,
160                 @SerializedName("minPos") long minPos, @SerializedName("playbackState") PlaybackState playbackState,
161                 long position, @Nullable String source, @SerializedName("subtitleIndex") int subtitleIndex,
162                 @SerializedName("subtitleList") List<SubtitleTrack> subtitleList,
163                 @SerializedName("videoIndex") int videoIndex, @SerializedName("videoList") List<VideoTrack> videoList) {
164         }
165     }
166
167     private static enum BouquetType {
168         ADSL,
169         UNKNOWN;
170     }
171
172     private static enum ChannelType {
173         REGULAR,
174         UNKNOWN;
175     }
176
177     private static record Service(long id, @Nullable String name,
178             @SerializedName("qualityLabel") @Nullable String qualityLabel,
179             @SerializedName("qualityName") @Nullable String qualityName, @SerializedName("sortInfo") int sortInfo,
180             @SerializedName("typeLabel") @Nullable String typeLabel,
181             @SerializedName("typeName") @Nullable String typeName, @Nullable String url) {
182     }
183
184     private static record Channel(@SerializedName("bouquetId") long bouquetId,
185             @SerializedName("bouquetName") @Nullable String bouquetName,
186             @SerializedName("bouquetType") BouquetType bouquetType,
187             @SerializedName("channelName") @Nullable String channelName,
188             @SerializedName("channelNumber") int channelNumber,
189             @SerializedName("channelSubNumber") int channelSubNumber,
190             @SerializedName("channelType") ChannelType channelType,
191             @SerializedName("channelUuid") @Nullable String channelUuid,
192             @SerializedName("currentServiceIndex") int currentServiceIndex,
193             @SerializedName("isTimeShifting") boolean isTimeShifting, List<Service> services,
194             @SerializedName("videoIsVisible") boolean videoIsVisible) {
195     }
196
197     public static record TvContext(@Nullable Channel channel, @Nullable PlayerDetails player) {
198     }
199
200     private final Map<Integer, String> subPaths = new HashMap<>();
201
202     public PlayerManager(FreeboxOsSession session) throws FreeboxException {
203         super(session, LoginManager.Permission.PLAYER, PlayerResponse.class,
204                 session.getUriBuilder().path(THING_PLAYER));
205         getDevices().stream().filter(Player::apiAvailable).forEach(player -> {
206             String baseUrl = player.baseUrl();
207             if (baseUrl != null) {
208                 subPaths.put(player.id, baseUrl);
209             }
210         });
211     }
212
213     public Status getPlayerStatus(int id) throws FreeboxException {
214         return getSingle(StatusResponse.class, subPaths.get(id), STATUS_PATH);
215     }
216
217     // The player API does not allow to directly request a given player like others api parts
218     @Override
219     public Player getDevice(int id) throws FreeboxException {
220         return getDevices().stream().filter(player -> player.id == id).findFirst().orElse(null);
221     }
222
223     public Configuration getConfig(int id) throws FreeboxException {
224         return getSingle(ConfigurationResponse.class, subPaths.get(id), SYSTEM_PATH);
225     }
226
227     public void sendKey(String ip, String code, String key, boolean longPress, int count) {
228         UriBuilder uriBuilder = UriBuilder.fromPath("pub").scheme("http").host(ip).path("remote_control");
229         uriBuilder.queryParam("code", code).queryParam("key", key);
230         if (longPress) {
231             uriBuilder.queryParam("long", true);
232         }
233         if (count > 1) {
234             uriBuilder.queryParam("repeat", count);
235         }
236         try {
237             session.execute(uriBuilder.build(), HttpMethod.GET, GenericResponse.class, null);
238         } catch (FreeboxException ignore) {
239             // This call does not return anything, we can safely ignore
240         }
241     }
242
243     public void reboot(int id) throws FreeboxException {
244         post(subPaths.get(id), SYSTEM_PATH, REBOOT_ACTION);
245     }
246 }