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.*;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.List;
20 import java.util.Locale;
23 import org.apache.commons.lang.StringUtils;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.amazonechocontrol.internal.handler.AccountHandler;
27 import org.openhab.binding.amazonechocontrol.internal.handler.EchoHandler;
28 import org.openhab.binding.amazonechocontrol.internal.handler.FlashBriefingProfileHandler;
29 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonBluetoothStates.BluetoothState;
30 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonBluetoothStates.PairedDevice;
31 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonDevices.Device;
32 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonMusicProvider;
33 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonNotificationSound;
34 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonPlaylists;
35 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonPlaylists.PlayList;
36 import org.openhab.core.thing.Channel;
37 import org.openhab.core.thing.Thing;
38 import org.openhab.core.thing.ThingRegistry;
39 import org.openhab.core.thing.ThingUID;
40 import org.openhab.core.thing.binding.ThingHandler;
41 import org.openhab.core.thing.type.ChannelTypeUID;
42 import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
43 import org.openhab.core.types.StateDescription;
44 import org.openhab.core.types.StateDescriptionFragmentBuilder;
45 import org.openhab.core.types.StateOption;
46 import org.osgi.service.component.annotations.Activate;
47 import org.osgi.service.component.annotations.Component;
48 import org.osgi.service.component.annotations.Reference;
51 * Dynamic channel state description provider.
52 * Overrides the state description for the controls, which receive its configuration in the runtime.
54 * @author Michael Geramb - Initial contribution
56 @Component(service = { DynamicStateDescriptionProvider.class, AmazonEchoDynamicStateDescriptionProvider.class })
58 public class AmazonEchoDynamicStateDescriptionProvider implements DynamicStateDescriptionProvider {
59 private final ThingRegistry thingRegistry;
62 public AmazonEchoDynamicStateDescriptionProvider(@Reference ThingRegistry thingRegistry) {
63 this.thingRegistry = thingRegistry;
66 public @Nullable ThingHandler findHandler(Channel channel) {
67 Thing thing = thingRegistry.get(channel.getUID().getThingUID());
71 ThingUID accountThingId = thing.getBridgeUID();
72 if (accountThingId == null) {
75 Thing accountThing = thingRegistry.get(accountThingId);
76 if (accountThing == null) {
79 AccountHandler accountHandler = (AccountHandler) accountThing.getHandler();
80 if (accountHandler == null) {
83 Connection connection = accountHandler.findConnection();
84 if (connection == null || !connection.getIsLoggedIn()) {
87 return thing.getHandler();
91 public @Nullable StateDescription getStateDescription(Channel channel,
92 @Nullable StateDescription originalStateDescription, @Nullable Locale locale) {
93 ChannelTypeUID channelTypeUID = channel.getChannelTypeUID();
94 if (channelTypeUID == null || !BINDING_ID.equals(channelTypeUID.getBindingId())) {
97 if (originalStateDescription == null) {
101 if (CHANNEL_TYPE_BLUETHOOTH_MAC.equals(channel.getChannelTypeUID())) {
102 EchoHandler handler = (EchoHandler) findHandler(channel);
103 if (handler == null) {
106 BluetoothState bluetoothState = handler.findBluetoothState();
107 if (bluetoothState == null) {
110 PairedDevice[] pairedDeviceList = bluetoothState.pairedDeviceList;
111 if (pairedDeviceList == null) {
115 List<StateOption> options = new ArrayList<>();
116 options.add(new StateOption("", ""));
117 for (PairedDevice device : pairedDeviceList) {
118 if (device == null) {
121 final String value = device.address;
122 if (value != null && device.friendlyName != null) {
123 options.add(new StateOption(value, device.friendlyName));
126 StateDescription result = StateDescriptionFragmentBuilder.create(originalStateDescription)
127 .withOptions(options).build().toStateDescription();
129 } else if (CHANNEL_TYPE_AMAZON_MUSIC_PLAY_LIST_ID.equals(channel.getChannelTypeUID())) {
130 EchoHandler handler = (EchoHandler) findHandler(channel);
131 if (handler == null) {
135 JsonPlaylists playLists = handler.findPlaylists();
136 if (playLists == null) {
140 List<StateOption> options = new ArrayList<>();
141 options.add(new StateOption("", ""));
142 Map<String, PlayList @Nullable []> playlistMap = playLists.playlists;
143 if (playlistMap != null) {
144 for (PlayList[] innerLists : playlistMap.values()) {
145 if (innerLists != null && innerLists.length > 0) {
146 PlayList playList = innerLists[0];
147 final String value = playList.playlistId;
148 if (value != null && playList.title != null) {
149 options.add(new StateOption(value,
150 String.format("%s (%d)", playList.title, playList.trackCount)));
155 StateDescription result = StateDescriptionFragmentBuilder.create(originalStateDescription)
156 .withOptions(options).build().toStateDescription();
158 } else if (CHANNEL_TYPE_PLAY_ALARM_SOUND.equals(channel.getChannelTypeUID())) {
159 EchoHandler handler = (EchoHandler) findHandler(channel);
160 if (handler == null) {
164 JsonNotificationSound[] notificationSounds = handler.findAlarmSounds();
165 if (notificationSounds == null) {
169 List<StateOption> options = new ArrayList<>();
170 options.add(new StateOption("", ""));
172 for (JsonNotificationSound notificationSound : notificationSounds) {
173 if (notificationSound != null && notificationSound.folder == null
174 && notificationSound.providerId != null && notificationSound.id != null
175 && notificationSound.displayName != null) {
176 String providerSoundId = notificationSound.providerId + ":" + notificationSound.id;
177 options.add(new StateOption(providerSoundId, notificationSound.displayName));
180 StateDescription result = StateDescriptionFragmentBuilder.create(originalStateDescription)
181 .withOptions(options).build().toStateDescription();
183 } else if (CHANNEL_TYPE_CHANNEL_PLAY_ON_DEVICE.equals(channel.getChannelTypeUID())) {
184 FlashBriefingProfileHandler handler = (FlashBriefingProfileHandler) findHandler(channel);
185 if (handler == null) {
188 AccountHandler accountHandler = handler.findAccountHandler();
189 if (accountHandler == null) {
192 List<Device> devices = accountHandler.getLastKnownDevices();
193 if (devices.isEmpty()) {
197 List<StateOption> options = new ArrayList<>();
198 options.add(new StateOption("", ""));
199 for (Device device : devices) {
200 final String value = device.serialNumber;
201 if (value != null && device.capabilities != null
202 && Arrays.asList(device.capabilities).contains("FLASH_BRIEFING")) {
203 options.add(new StateOption(value, device.accountName));
206 return StateDescriptionFragmentBuilder.create(originalStateDescription).withOptions(options).build()
207 .toStateDescription();
208 } else if (CHANNEL_TYPE_MUSIC_PROVIDER_ID.equals(channel.getChannelTypeUID())) {
209 EchoHandler handler = (EchoHandler) findHandler(channel);
210 if (handler == null) {
213 List<JsonMusicProvider> musicProviders = handler.findMusicProviders();
214 if (musicProviders == null) {
218 List<StateOption> options = new ArrayList<>();
219 for (JsonMusicProvider musicProvider : musicProviders) {
220 List<String> properties = musicProvider.supportedProperties;
221 String providerId = musicProvider.id;
222 String displayName = musicProvider.displayName;
223 if (properties != null && properties.contains("Alexa.Music.PlaySearchPhrase")
224 && StringUtils.isNotEmpty(providerId)
225 && StringUtils.equals(musicProvider.availability, "AVAILABLE")
226 && StringUtils.isNotEmpty(displayName) && providerId != null) {
227 options.add(new StateOption(providerId, displayName));
230 return StateDescriptionFragmentBuilder.create(originalStateDescription).withOptions(options).build()
231 .toStateDescription();
232 } else if (CHANNEL_TYPE_START_COMMAND.equals(channel.getChannelTypeUID())) {
233 EchoHandler handler = (EchoHandler) findHandler(channel);
234 if (handler == null) {
237 AccountHandler account = handler.findAccount();
238 if (account == null) {
241 List<FlashBriefingProfileHandler> flashbriefings = account.getFlashBriefingProfileHandlers();
242 if (flashbriefings.isEmpty()) {
246 List<StateOption> options = new ArrayList<>();
247 options.addAll(originalStateDescription.getOptions());
249 for (FlashBriefingProfileHandler flashBriefing : flashbriefings) {
250 String value = FLASH_BRIEFING_COMMAND_PREFIX + flashBriefing.getThing().getUID().getId();
251 String displayName = flashBriefing.getThing().getLabel();
252 options.add(new StateOption(value, displayName));
254 return StateDescriptionFragmentBuilder.create(originalStateDescription).withOptions(options).build()
255 .toStateDescription();