2 * Copyright (c) 2010-2020 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.amazonechocontrol.internal;
15 import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.BINDING_ID;
16 import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_TYPE_AMAZON_MUSIC_PLAY_LIST_ID;
17 import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_TYPE_BLUETHOOTH_MAC;
18 import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_TYPE_CHANNEL_PLAY_ON_DEVICE;
19 import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_TYPE_MUSIC_PROVIDER_ID;
20 import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_TYPE_PLAY_ALARM_SOUND;
21 import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_TYPE_START_COMMAND;
22 import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.FLASH_BRIEFING_COMMAND_PREFIX;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.List;
27 import java.util.Locale;
30 import org.apache.commons.lang.StringUtils;
31 import org.eclipse.jdt.annotation.NonNullByDefault;
32 import org.eclipse.jdt.annotation.Nullable;
33 import org.openhab.binding.amazonechocontrol.internal.handler.AccountHandler;
34 import org.openhab.binding.amazonechocontrol.internal.handler.EchoHandler;
35 import org.openhab.binding.amazonechocontrol.internal.handler.FlashBriefingProfileHandler;
36 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonBluetoothStates.BluetoothState;
37 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonBluetoothStates.PairedDevice;
38 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonDevices.Device;
39 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonMusicProvider;
40 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonNotificationSound;
41 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonPlaylists;
42 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonPlaylists.PlayList;
43 import org.openhab.core.thing.Channel;
44 import org.openhab.core.thing.Thing;
45 import org.openhab.core.thing.ThingRegistry;
46 import org.openhab.core.thing.ThingUID;
47 import org.openhab.core.thing.binding.ThingHandler;
48 import org.openhab.core.thing.type.ChannelTypeUID;
49 import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
50 import org.openhab.core.types.StateDescription;
51 import org.openhab.core.types.StateDescriptionFragmentBuilder;
52 import org.openhab.core.types.StateOption;
53 import org.osgi.service.component.annotations.Activate;
54 import org.osgi.service.component.annotations.Component;
55 import org.osgi.service.component.annotations.Reference;
58 * Dynamic channel state description provider.
59 * Overrides the state description for the controls, which receive its configuration in the runtime.
61 * @author Michael Geramb - Initial contribution
63 @Component(service = { DynamicStateDescriptionProvider.class, AmazonEchoDynamicStateDescriptionProvider.class })
65 public class AmazonEchoDynamicStateDescriptionProvider implements DynamicStateDescriptionProvider {
66 private final ThingRegistry thingRegistry;
69 public AmazonEchoDynamicStateDescriptionProvider(@Reference ThingRegistry thingRegistry) {
70 this.thingRegistry = thingRegistry;
73 public @Nullable ThingHandler findHandler(Channel channel) {
74 Thing thing = thingRegistry.get(channel.getUID().getThingUID());
78 ThingUID accountThingId = thing.getBridgeUID();
79 if (accountThingId == null) {
82 Thing accountThing = thingRegistry.get(accountThingId);
83 if (accountThing == null) {
86 AccountHandler accountHandler = (AccountHandler) accountThing.getHandler();
87 if (accountHandler == null) {
90 Connection connection = accountHandler.findConnection();
91 if (connection == null || !connection.getIsLoggedIn()) {
94 return thing.getHandler();
98 public @Nullable StateDescription getStateDescription(Channel channel,
99 @Nullable StateDescription originalStateDescription, @Nullable Locale locale) {
100 ChannelTypeUID channelTypeUID = channel.getChannelTypeUID();
101 if (channelTypeUID == null || !BINDING_ID.equals(channelTypeUID.getBindingId())) {
104 if (originalStateDescription == null) {
108 if (CHANNEL_TYPE_BLUETHOOTH_MAC.equals(channel.getChannelTypeUID())) {
109 EchoHandler handler = (EchoHandler) findHandler(channel);
110 if (handler == null) {
111 return originalStateDescription;
113 BluetoothState bluetoothState = handler.findBluetoothState();
114 if (bluetoothState == null) {
115 return originalStateDescription;
117 PairedDevice[] pairedDeviceList = bluetoothState.pairedDeviceList;
118 if (pairedDeviceList == null) {
119 return originalStateDescription;
122 ArrayList<StateOption> options = new ArrayList<>();
123 options.add(new StateOption("", ""));
124 for (PairedDevice device : pairedDeviceList) {
125 if (device == null) {
128 final String value = device.address;
129 if (value != null && device.friendlyName != null) {
130 options.add(new StateOption(value, device.friendlyName));
133 StateDescription result = StateDescriptionFragmentBuilder.create(originalStateDescription)
134 .withOptions(options).build().toStateDescription();
136 } else if (CHANNEL_TYPE_AMAZON_MUSIC_PLAY_LIST_ID.equals(channel.getChannelTypeUID())) {
137 EchoHandler handler = (EchoHandler) findHandler(channel);
138 if (handler == null) {
139 return originalStateDescription;
142 JsonPlaylists playLists = handler.findPlaylists();
143 if (playLists == null) {
144 return originalStateDescription;
147 ArrayList<StateOption> options = new ArrayList<>();
148 options.add(new StateOption("", ""));
150 Map<String, @Nullable PlayList @Nullable []> playlistMap = playLists.playlists;
151 if (playlistMap != null) {
152 for (PlayList[] innerLists : playlistMap.values()) {
153 if (innerLists != null && innerLists.length > 0) {
154 PlayList playList = innerLists[0];
155 final String value = playList.playlistId;
156 if (value != null && playList.title != null) {
157 options.add(new StateOption(value,
158 String.format("%s (%d)", playList.title, playList.trackCount)));
163 StateDescription result = StateDescriptionFragmentBuilder.create(originalStateDescription)
164 .withOptions(options).build().toStateDescription();
166 } else if (CHANNEL_TYPE_PLAY_ALARM_SOUND.equals(channel.getChannelTypeUID())) {
167 EchoHandler handler = (EchoHandler) findHandler(channel);
168 if (handler == null) {
169 return originalStateDescription;
172 JsonNotificationSound[] notificationSounds = handler.findAlarmSounds();
173 if (notificationSounds == null) {
174 return originalStateDescription;
177 ArrayList<StateOption> options = new ArrayList<>();
178 options.add(new StateOption("", ""));
180 for (JsonNotificationSound notificationSound : notificationSounds) {
181 if (notificationSound != null && notificationSound.folder == null
182 && notificationSound.providerId != null && notificationSound.id != null
183 && notificationSound.displayName != null) {
184 String providerSoundId = notificationSound.providerId + ":" + notificationSound.id;
185 options.add(new StateOption(providerSoundId, notificationSound.displayName));
188 StateDescription result = StateDescriptionFragmentBuilder.create(originalStateDescription)
189 .withOptions(options).build().toStateDescription();
191 } else if (CHANNEL_TYPE_CHANNEL_PLAY_ON_DEVICE.equals(channel.getChannelTypeUID())) {
192 FlashBriefingProfileHandler handler = (FlashBriefingProfileHandler) findHandler(channel);
193 if (handler == null) {
194 return originalStateDescription;
196 AccountHandler accountHandler = handler.findAccountHandler();
197 if (accountHandler == null) {
198 return originalStateDescription;
200 List<Device> devices = accountHandler.getLastKnownDevices();
201 if (devices.isEmpty()) {
202 return originalStateDescription;
205 ArrayList<StateOption> options = new ArrayList<>();
206 options.add(new StateOption("", ""));
207 for (Device device : devices) {
208 final String value = device.serialNumber;
209 if (value != null && device.capabilities != null
210 && Arrays.asList(device.capabilities).contains("FLASH_BRIEFING")) {
211 options.add(new StateOption(value, device.accountName));
214 return StateDescriptionFragmentBuilder.create(originalStateDescription).withOptions(options).build()
215 .toStateDescription();
216 } else if (CHANNEL_TYPE_MUSIC_PROVIDER_ID.equals(channel.getChannelTypeUID())) {
217 EchoHandler handler = (EchoHandler) findHandler(channel);
218 if (handler == null) {
219 return originalStateDescription;
221 List<JsonMusicProvider> musicProviders = handler.findMusicProviders();
222 if (musicProviders == null) {
223 return originalStateDescription;
226 ArrayList<StateOption> options = new ArrayList<>();
227 for (JsonMusicProvider musicProvider : musicProviders) {
229 List<@Nullable String> properties = musicProvider.supportedProperties;
230 String providerId = musicProvider.id;
231 String displayName = musicProvider.displayName;
232 if (properties != null && properties.contains("Alexa.Music.PlaySearchPhrase")
233 && StringUtils.isNotEmpty(providerId)
234 && StringUtils.equals(musicProvider.availability, "AVAILABLE")
235 && StringUtils.isNotEmpty(displayName) && providerId != null) {
236 options.add(new StateOption(providerId, displayName));
239 return StateDescriptionFragmentBuilder.create(originalStateDescription).withOptions(options).build()
240 .toStateDescription();
241 } else if (CHANNEL_TYPE_START_COMMAND.equals(channel.getChannelTypeUID())) {
242 EchoHandler handler = (EchoHandler) findHandler(channel);
243 if (handler == null) {
244 return originalStateDescription;
246 AccountHandler account = handler.findAccount();
247 if (account == null) {
248 return originalStateDescription;
250 List<FlashBriefingProfileHandler> flashbriefings = account.getFlashBriefingProfileHandlers();
251 if (flashbriefings.isEmpty()) {
252 return originalStateDescription;
255 ArrayList<StateOption> options = new ArrayList<>();
256 options.addAll(originalStateDescription.getOptions());
258 for (FlashBriefingProfileHandler flashBriefing : flashbriefings) {
259 String value = FLASH_BRIEFING_COMMAND_PREFIX + flashBriefing.getThing().getUID().getId();
260 String displayName = flashBriefing.getThing().getLabel();
261 options.add(new StateOption(value, displayName));
263 return StateDescriptionFragmentBuilder.create(originalStateDescription).withOptions(options).build()
264 .toStateDescription();