]> git.basschouten.com Git - openhab-addons.git/blob
8f1da9adedbc6c05577ce51e736c9e6175c8c3f3
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.transform.basicprofiles.internal.factory;
14
15 import static org.openhab.transform.basicprofiles.internal.BasicProfilesConstants.SCOPE;
16
17 import java.util.Collection;
18 import java.util.Locale;
19 import java.util.Map;
20 import java.util.Set;
21 import java.util.concurrent.ConcurrentHashMap;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.core.i18n.LocalizedKey;
26 import org.openhab.core.i18n.TimeZoneProvider;
27 import org.openhab.core.items.ItemRegistry;
28 import org.openhab.core.library.CoreItemFactory;
29 import org.openhab.core.thing.Channel;
30 import org.openhab.core.thing.DefaultSystemChannelTypeProvider;
31 import org.openhab.core.thing.profiles.Profile;
32 import org.openhab.core.thing.profiles.ProfileAdvisor;
33 import org.openhab.core.thing.profiles.ProfileCallback;
34 import org.openhab.core.thing.profiles.ProfileContext;
35 import org.openhab.core.thing.profiles.ProfileFactory;
36 import org.openhab.core.thing.profiles.ProfileType;
37 import org.openhab.core.thing.profiles.ProfileTypeBuilder;
38 import org.openhab.core.thing.profiles.ProfileTypeProvider;
39 import org.openhab.core.thing.profiles.ProfileTypeUID;
40 import org.openhab.core.thing.profiles.i18n.ProfileTypeI18nLocalizationService;
41 import org.openhab.core.thing.type.ChannelType;
42 import org.openhab.core.util.BundleResolver;
43 import org.openhab.transform.basicprofiles.internal.profiles.DebounceCountingStateProfile;
44 import org.openhab.transform.basicprofiles.internal.profiles.DebounceTimeStateProfile;
45 import org.openhab.transform.basicprofiles.internal.profiles.GenericCommandTriggerProfile;
46 import org.openhab.transform.basicprofiles.internal.profiles.GenericToggleSwitchTriggerProfile;
47 import org.openhab.transform.basicprofiles.internal.profiles.InvertStateProfile;
48 import org.openhab.transform.basicprofiles.internal.profiles.RoundStateProfile;
49 import org.openhab.transform.basicprofiles.internal.profiles.StateFilterProfile;
50 import org.openhab.transform.basicprofiles.internal.profiles.ThresholdStateProfile;
51 import org.openhab.transform.basicprofiles.internal.profiles.TimeRangeCommandProfile;
52 import org.osgi.framework.Bundle;
53 import org.osgi.service.component.annotations.Activate;
54 import org.osgi.service.component.annotations.Component;
55 import org.osgi.service.component.annotations.Reference;
56
57 /**
58  * The {@link BasicProfilesFactory} is responsible for creating profiles.
59  *
60  * @author Christoph Weitkamp - Initial contribution
61  */
62 @Component(service = { ProfileFactory.class, ProfileTypeProvider.class })
63 @NonNullByDefault
64 public class BasicProfilesFactory implements ProfileFactory, ProfileTypeProvider, ProfileAdvisor {
65
66     public static final ProfileTypeUID GENERIC_COMMAND_UID = new ProfileTypeUID(SCOPE, "generic-command");
67     public static final ProfileTypeUID GENERIC_TOGGLE_SWITCH_UID = new ProfileTypeUID(SCOPE, "toggle-switch");
68     public static final ProfileTypeUID DEBOUNCE_COUNTING_UID = new ProfileTypeUID(SCOPE, "debounce-counting");
69     public static final ProfileTypeUID DEBOUNCE_TIME_UID = new ProfileTypeUID(SCOPE, "debounce-time");
70     public static final ProfileTypeUID INVERT_UID = new ProfileTypeUID(SCOPE, "invert");
71     public static final ProfileTypeUID ROUND_UID = new ProfileTypeUID(SCOPE, "round");
72     public static final ProfileTypeUID THRESHOLD_UID = new ProfileTypeUID(SCOPE, "threshold");
73     public static final ProfileTypeUID TIME_RANGE_COMMAND_UID = new ProfileTypeUID(SCOPE, "time-range-command");
74     public static final ProfileTypeUID STATE_FILTER_UID = new ProfileTypeUID(SCOPE, "state-filter");
75
76     private static final ProfileType PROFILE_TYPE_GENERIC_COMMAND = ProfileTypeBuilder
77             .newTrigger(GENERIC_COMMAND_UID, "Generic Command") //
78             .withSupportedItemTypes(CoreItemFactory.DIMMER, CoreItemFactory.NUMBER, CoreItemFactory.PLAYER,
79                     CoreItemFactory.ROLLERSHUTTER, CoreItemFactory.SWITCH) //
80             .build();
81     private static final ProfileType PROFILE_TYPE_GENERIC_TOGGLE_SWITCH = ProfileTypeBuilder
82             .newTrigger(GENERIC_TOGGLE_SWITCH_UID, "Generic Toggle Switch") //
83             .withSupportedItemTypes(CoreItemFactory.COLOR, CoreItemFactory.DIMMER, CoreItemFactory.SWITCH) //
84             .build();
85     private static final ProfileType PROFILE_TYPE_DEBOUNCE_COUNTING = ProfileTypeBuilder
86             .newState(DEBOUNCE_COUNTING_UID, "Debounce (Counting)").build();
87     private static final ProfileType PROFILE_TYPE_DEBOUNCE_TIME = ProfileTypeBuilder
88             .newState(DEBOUNCE_TIME_UID, "Debounce (Time)").build();
89     private static final ProfileType PROFILE_TYPE_INVERT = ProfileTypeBuilder.newState(INVERT_UID, "Invert / Negate")
90             .withSupportedItemTypes(CoreItemFactory.CONTACT, CoreItemFactory.DIMMER, CoreItemFactory.NUMBER,
91                     CoreItemFactory.PLAYER, CoreItemFactory.ROLLERSHUTTER, CoreItemFactory.SWITCH) //
92             .withSupportedItemTypesOfChannel(CoreItemFactory.CONTACT, CoreItemFactory.DIMMER, CoreItemFactory.NUMBER,
93                     CoreItemFactory.PLAYER, CoreItemFactory.ROLLERSHUTTER, CoreItemFactory.SWITCH) //
94             .build();
95     private static final ProfileType PROFILE_TYPE_ROUND = ProfileTypeBuilder.newState(ROUND_UID, "Round")
96             .withSupportedItemTypes(CoreItemFactory.NUMBER) //
97             .withSupportedItemTypesOfChannel(CoreItemFactory.NUMBER) //
98             .build();
99     private static final ProfileType PROFILE_TYPE_THRESHOLD = ProfileTypeBuilder.newState(THRESHOLD_UID, "Threshold") //
100             .withSupportedItemTypesOfChannel(CoreItemFactory.DIMMER, CoreItemFactory.NUMBER) //
101             .withSupportedItemTypes(CoreItemFactory.SWITCH) //
102             .build();
103     private static final ProfileType PROFILE_TYPE_TIME_RANGE_COMMAND = ProfileTypeBuilder
104             .newState(TIME_RANGE_COMMAND_UID, "Time Range Command") //
105             .withSupportedItemTypes(CoreItemFactory.SWITCH) //
106             .withSupportedChannelTypeUIDs(DefaultSystemChannelTypeProvider.SYSTEM_CHANNEL_TYPE_UID_MOTION) //
107             .build();
108     private static final ProfileType PROFILE_STATE_FILTER = ProfileTypeBuilder
109             .newState(STATE_FILTER_UID, "Filter handler state updates based on any item state").build();
110
111     private static final Set<ProfileTypeUID> SUPPORTED_PROFILE_TYPE_UIDS = Set.of(GENERIC_COMMAND_UID,
112             GENERIC_TOGGLE_SWITCH_UID, DEBOUNCE_COUNTING_UID, DEBOUNCE_TIME_UID, INVERT_UID, ROUND_UID, THRESHOLD_UID,
113             TIME_RANGE_COMMAND_UID, STATE_FILTER_UID);
114     private static final Set<ProfileType> SUPPORTED_PROFILE_TYPES = Set.of(PROFILE_TYPE_GENERIC_COMMAND,
115             PROFILE_TYPE_GENERIC_TOGGLE_SWITCH, PROFILE_TYPE_DEBOUNCE_COUNTING, PROFILE_TYPE_DEBOUNCE_TIME,
116             PROFILE_TYPE_INVERT, PROFILE_TYPE_ROUND, PROFILE_TYPE_THRESHOLD, PROFILE_TYPE_TIME_RANGE_COMMAND,
117             PROFILE_STATE_FILTER);
118
119     private final Map<LocalizedKey, ProfileType> localizedProfileTypeCache = new ConcurrentHashMap<>();
120
121     private final ProfileTypeI18nLocalizationService profileTypeI18nLocalizationService;
122     private final Bundle bundle;
123     private final ItemRegistry itemRegistry;
124     private final TimeZoneProvider timeZoneProvider;
125
126     @Activate
127     public BasicProfilesFactory(final @Reference ProfileTypeI18nLocalizationService profileTypeI18nLocalizationService,
128             final @Reference BundleResolver bundleResolver, @Reference ItemRegistry itemRegistry,
129             @Reference TimeZoneProvider timeZoneProvider) {
130         this.profileTypeI18nLocalizationService = profileTypeI18nLocalizationService;
131         this.bundle = bundleResolver.resolveBundle(BasicProfilesFactory.class);
132         this.itemRegistry = itemRegistry;
133         this.timeZoneProvider = timeZoneProvider;
134     }
135
136     @Override
137     public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback,
138             ProfileContext context) {
139         if (GENERIC_COMMAND_UID.equals(profileTypeUID)) {
140             return new GenericCommandTriggerProfile(callback, context);
141         } else if (GENERIC_TOGGLE_SWITCH_UID.equals(profileTypeUID)) {
142             return new GenericToggleSwitchTriggerProfile(callback, context);
143         } else if (DEBOUNCE_COUNTING_UID.equals(profileTypeUID)) {
144             return new DebounceCountingStateProfile(callback, context);
145         } else if (DEBOUNCE_TIME_UID.equals(profileTypeUID)) {
146             return new DebounceTimeStateProfile(callback, context);
147         } else if (INVERT_UID.equals(profileTypeUID)) {
148             return new InvertStateProfile(callback);
149         } else if (ROUND_UID.equals(profileTypeUID)) {
150             return new RoundStateProfile(callback, context);
151         } else if (THRESHOLD_UID.equals(profileTypeUID)) {
152             return new ThresholdStateProfile(callback, context);
153         } else if (TIME_RANGE_COMMAND_UID.equals(profileTypeUID)) {
154             return new TimeRangeCommandProfile(callback, context, timeZoneProvider);
155         } else if (STATE_FILTER_UID.equals(profileTypeUID)) {
156             return new StateFilterProfile(callback, context, itemRegistry);
157         }
158         return null;
159     }
160
161     @Override
162     public Collection<ProfileType> getProfileTypes(@Nullable Locale locale) {
163         return SUPPORTED_PROFILE_TYPES.stream().map(p -> createLocalizedProfileType(p, locale)).toList();
164     }
165
166     @Override
167     public Collection<ProfileTypeUID> getSupportedProfileTypeUIDs() {
168         return SUPPORTED_PROFILE_TYPE_UIDS;
169     }
170
171     @Override
172     public @Nullable ProfileTypeUID getSuggestedProfileTypeUID(ChannelType channelType, @Nullable String itemType) {
173         return null;
174     }
175
176     @Override
177     public @Nullable ProfileTypeUID getSuggestedProfileTypeUID(Channel channel, @Nullable String itemType) {
178         return null;
179     }
180
181     private ProfileType createLocalizedProfileType(ProfileType profileType, @Nullable Locale locale) {
182         final LocalizedKey localizedKey = new LocalizedKey(profileType.getUID(),
183                 locale != null ? locale.toLanguageTag() : null);
184
185         final ProfileType cachedlocalizedProfileType = localizedProfileTypeCache.get(localizedKey);
186         if (cachedlocalizedProfileType != null) {
187             return cachedlocalizedProfileType;
188         }
189
190         final ProfileType localizedProfileType = profileTypeI18nLocalizationService.createLocalizedProfileType(bundle,
191                 profileType, locale);
192         if (localizedProfileType != null) {
193             localizedProfileTypeCache.put(localizedKey, localizedProfileType);
194             return localizedProfileType;
195         } else {
196             return profileType;
197         }
198     }
199 }