]> git.basschouten.com Git - openhab-addons.git/blob
c88a97c392f1e25c9618dc0db648e5b90f8c4926
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.hdpowerview.internal.database;
14
15 import java.util.Arrays;
16 import java.util.Map;
17 import java.util.function.Function;
18 import java.util.stream.Collectors;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 /**
26  * Class containing the database of all known shade 'types' and their respective 'capabilities'.
27  *
28  * If user systems detect shade types that are not in the database, then this class can issue logger warning messages
29  * indicating such absence, and prompting the user to report it to developers so that the database and the respective
30  * binding functionality can (hopefully) be extended over time.
31  *
32  * @author Andrew Fiddian-Green - Initial Contribution
33  */
34 @NonNullByDefault
35 public class ShadeCapabilitiesDatabase {
36
37     private final Logger logger = LoggerFactory.getLogger(ShadeCapabilitiesDatabase.class);
38
39     /*
40      * Database of known shade capabilities.
41      */
42     private static final Map<Integer, Capabilities> CAPABILITIES_DATABASE = Arrays.asList(
43     // @formatter:off
44             new Capabilities( 0).primary()                                                       .text("Bottom Up"),
45             new Capabilities( 1).primary()        .tiltOnClosed()                                .text("Bottom Up Tilt 90°"),
46             new Capabilities( 2).primary()        .tiltAnywhere().tilt180()                      .text("Bottom Up Tilt 180°"),
47             new Capabilities( 3).primary()                                                       .text("Vertical"),
48             new Capabilities( 4).primary()        .tiltAnywhere().tilt180()                      .text("Vertical Tilt 180°"),
49             new Capabilities( 5)                  .tiltAnywhere().tilt180()                      .text("Tilt Only 180°"),
50             new Capabilities( 6).primaryInverted()                                               .text("Top Down"),
51             new Capabilities( 7).primary()                                 .secondary()          .text("Top Down Bottom Up"),
52             new Capabilities( 8).primary()                                 .secondaryOverlapped().text("Dual Overlapped"),
53             // note: for the following capabilities entry the 'tiltOnClosed()' applies to the primary shade
54             new Capabilities( 9).primary()        .tiltOnClosed()          .secondaryOverlapped().text("Dual Overlapped Tilt 90°"),
55             new Capabilities(10).primary()        .tiltOnClosed().tilt180().secondaryOverlapped().text("Dual Overlapped Tilt 180°"),
56     // @formatter:on
57             new Capabilities()).stream().collect(Collectors.toMap(Capabilities::getValue, Function.identity()));
58
59     /*
60      * Database of known shade types and corresponding capabilities.
61      */
62     private static final Map<Integer, Type> TYPE_DATABASE = Arrays.asList(
63     // @formatter:off
64             new Type( 1).capabilities(0).text("Roller / Solar"),
65             new Type( 4).capabilities(0).text("Roman"),
66             new Type( 5).capabilities(0).text("Bottom Up"),
67             new Type( 6).capabilities(0).text("Duette"),
68             new Type( 7).capabilities(6).text("Top Down"),
69             new Type( 8).capabilities(7).text("Duette Top Down Bottom Up"),
70             new Type( 9).capabilities(7).text("Duette DuoLite Top Down Bottom Up"),
71             new Type(18).capabilities(1).text("Pirouette"),
72             new Type(23).capabilities(1).text("Silhouette"),
73             new Type(26).capabilities(3).text("Skyline Panel, Left Stack"),
74             new Type(27).capabilities(3).text("Skyline Panel, Right Stack"),
75             new Type(28).capabilities(3).text("Skyline Panel, Split Stack"),
76             new Type(31).capabilities(0).text("Vignette"),
77             new Type(33).capabilities(7).text("Duette Architella"),
78             new Type(38).capabilities(9).text("Silhouette Duolite"),
79             new Type(42).capabilities(0).text("M25T Roller Blind"),
80             new Type(43).capabilities(1).text("Facette"),
81             // note: the following shade type has the functionality of a capabilities 1 shade
82             new Type(44).capabilities(0).text("Twist").capabilitiesOverride(1),
83             new Type(47).capabilities(7).text("Pleated Top Down Bottom Up"),
84             new Type(49).capabilities(0).text("AC Roller"),
85             new Type(51).capabilities(2).text("Venetian"),
86             // note: sometimes shade type 54/55/56 wrongly report capabilities:3 so force capabilities:4
87             new Type(54).capabilities(4).text("Vertical Slats Left Stack").capabilitiesOverride(4),
88             new Type(55).capabilities(4).text("Vertical Slats Right Stack").capabilitiesOverride(4),
89             new Type(56).capabilities(4).text("Vertical Slats Split Stack").capabilitiesOverride(4),
90             new Type(62).capabilities(2).text("Venetian"),
91             new Type(65).capabilities(8).text("Vignette Duolite"),
92             new Type(66).capabilities(5).text("Shutter"),
93             new Type(69).capabilities(3).text("Curtain Left Stack"),
94             new Type(70).capabilities(3).text("Curtain Right Stack"),
95             new Type(71).capabilities(3).text("Curtain Split Stack"),
96             new Type(79).capabilities(8).text("Duolite Lift"),
97     // @formatter:on
98             new Type()).stream().collect(Collectors.toMap(Type::getValue, Function.identity()));
99
100     /**
101      * Base class that is extended by Type and Capabilities classes.
102      *
103      * @author Andrew Fiddian-Green - Initial Contribution
104      */
105     private static class Base {
106         protected int intValue = -1;
107         protected String text = "-- not in database --";
108
109         public Integer getValue() {
110             return intValue;
111         }
112
113         @Override
114         public String toString() {
115             return String.format("%s (%d)", text, intValue);
116         }
117     }
118
119     /**
120      * Describes a shade type entry in the database; implements 'capabilities' parameter.
121      *
122      * @author Andrew Fiddian-Green - Initial Contribution
123      */
124     public static class Type extends Base {
125         private int capabilities = -1;
126         private int capabilitiesOverride = -1;
127
128         protected Type() {
129         }
130
131         protected Type(int type) {
132             intValue = type;
133         }
134
135         protected Type text(String text) {
136             this.text = text;
137             return this;
138         }
139
140         protected Type capabilities(int capabilities) {
141             this.capabilities = capabilities;
142             return this;
143         }
144
145         protected Type capabilitiesOverride(int capabilitiesOverride) {
146             this.capabilitiesOverride = capabilitiesOverride;
147             return this;
148         }
149
150         /**
151          * Get shade types's 'capabilities'.
152          *
153          * @return 'capabilities'.
154          */
155         public int getCapabilities() {
156             return capabilities;
157         }
158
159         /**
160          * Get shade's overridden 'capabilities'.
161          *
162          * @return 'capabilitiesOverride'.
163          */
164         public int getCapabilitiesOverride() {
165             return capabilitiesOverride;
166         }
167     }
168
169     /**
170      * Describes a shade 'capabilities' entry in the database; adds properties indicating its supported functionality.
171      *
172      * @author Andrew Fiddian-Green - Initial Contribution
173      */
174     public static class Capabilities extends Base {
175         private boolean supportsPrimary;
176         private boolean supportsSecondary;
177         private boolean supportsTiltOnClosed;
178         private boolean supportsTiltAnywhere;
179         private boolean supportsSecondaryOverlapped;
180         private boolean primaryInverted;
181         private boolean tilt180Degrees;
182
183         public Capabilities() {
184         }
185
186         protected Capabilities secondaryOverlapped() {
187             supportsSecondaryOverlapped = true;
188             return this;
189         }
190
191         protected Capabilities(int capabilities) {
192             intValue = capabilities;
193         }
194
195         protected Capabilities text(String text) {
196             this.text = text;
197             return this;
198         }
199
200         protected Capabilities primary() {
201             supportsPrimary = true;
202             return this;
203         }
204
205         protected Capabilities tiltOnClosed() {
206             supportsTiltOnClosed = true;
207             return this;
208         }
209
210         protected Capabilities secondary() {
211             supportsSecondary = true;
212             return this;
213         }
214
215         protected Capabilities tiltAnywhere() {
216             supportsTiltAnywhere = true;
217             return this;
218         }
219
220         protected Capabilities primaryInverted() {
221             supportsPrimary = true;
222             primaryInverted = true;
223             return this;
224         }
225
226         protected Capabilities tilt180() {
227             tilt180Degrees = true;
228             return this;
229         }
230
231         /**
232          * Check if the Capabilities class instance supports a primary shade.
233          *
234          * @return true if it supports a primary shade.
235          */
236         public boolean supportsPrimary() {
237             return supportsPrimary;
238         }
239
240         /**
241          * Check if the Capabilities class instance supports a vane/tilt function (by means of a second motor).
242          *
243          * @return true if it supports a vane/tilt function (by means of a second motor).
244          */
245         public boolean supportsTiltAnywhere() {
246             return supportsTiltAnywhere;
247         }
248
249         /**
250          * Check if the Capabilities class instance supports a secondary shade.
251          *
252          * @return true if it supports a secondary shade.
253          */
254         public boolean supportsSecondary() {
255             return supportsSecondary;
256         }
257
258         /**
259          * Check if the Capabilities class instance if the primary shade is inverted.
260          *
261          * @return true if the primary shade is inverted.
262          */
263         public boolean isPrimaryInverted() {
264             return primaryInverted;
265         }
266
267         /**
268          * Check if the Capabilities class instance supports 'tilt on closed'.
269          *
270          * Note: Simple bottom up or vertical shades that do not have independent vane controls, can be tilted in a
271          * simple way, only when they are fully closed, by moving the shade motor a bit further.
272          *
273          * @return true if the it supports tilt on closed.
274          */
275         public boolean supportsTiltOnClosed() {
276             return supportsTiltOnClosed && !supportsTiltAnywhere;
277         }
278
279         /**
280          * Check if the Capabilities class instance supports 180 degrees tilt.
281          *
282          * @return true if the tilt range is 180 degrees.
283          */
284         public boolean supportsTilt180() {
285             return tilt180Degrees;
286         }
287
288         /**
289          * Check if the Capabilities class instance supports an overlapped secondary shade.
290          * e.g. a 'DuoLite' or blackout shade.
291          *
292          * @return true if the shade supports a secondary overlapped shade.
293          */
294         public boolean supportsSecondaryOverlapped() {
295             return supportsSecondaryOverlapped;
296         }
297     }
298
299     /**
300      * Determines if a given shade 'type' is in the database.
301      *
302      * @param type the shade 'type' parameter.
303      * @return true if the shade 'type' is known.
304      */
305     public boolean isTypeInDatabase(int type) {
306         return TYPE_DATABASE.containsKey(type);
307     }
308
309     /**
310      * Determines if a given 'capabilities' value is in the database.
311      *
312      * @param capabilities the shade 'capabilities' parameter
313      * @return true if the 'capabilities' value is known
314      */
315     public boolean isCapabilitiesInDatabase(int capabilities) {
316         return CAPABILITIES_DATABASE.containsKey(capabilities);
317     }
318
319     /**
320      * Return a Type class instance that corresponds to the given 'type' parameter.
321      *
322      * @param type the shade 'type' parameter.
323      * @return corresponding instance of Type class.
324      */
325     public Type getType(int type) {
326         return TYPE_DATABASE.getOrDefault(type, new Type());
327     }
328
329     /**
330      * Return a Capabilities class instance that corresponds to the given 'capabilitiesId' parameter. If the
331      * 'capabilitiesId' parameter is for a valid capabilities entry in the database, then that respective Capabilities
332      * class instance is returned. Otherwise a blank Capabilities class instance is returned.
333      *
334      * @param capabilitiesId the target capabilities Id.
335      * @return corresponding Capabilities class instance.
336      */
337     public Capabilities getCapabilities(@Nullable Integer capabilitiesId) {
338         return CAPABILITIES_DATABASE.getOrDefault(capabilitiesId != null ? capabilitiesId.intValue() : -1,
339                 new Capabilities());
340     }
341
342     /**
343      * Return a Capabilities class instance that corresponds to the given 'typeId' parameter.
344      * <p>
345      * <ul>
346      * <li>If the 'typeId' parameter is a valid type in the database, and it has a 'capabilitiesOverride' value, then an
347      * instance of the respective overridden Capabilities class is returned.
348      * <li>Otherwise if the 'capabilitiesId' parameter is for a valid capabilities entry in the database, then that
349      * respective Capabilities class instance is returned.
350      * <li>Otherwise if the type is a valid type in the database, then its 'capabilities' instance is returned.
351      * <li>Otherwise a default Capabilities '0' class instance is returned.
352      * </ul>
353      * <p>
354      *
355      * @param typeId the target shade type Id (to check if it has a 'capabilitiesOverride' value).
356      * @param capabilitiesId the target capabilities value (when type Id does not have a 'capabilitiesOverride').
357      * @return corresponding Capabilities class instance.
358      */
359     public Capabilities getCapabilities(int typeId, @Nullable Integer capabilitiesId) {
360         Type type = TYPE_DATABASE.getOrDefault(typeId, new Type());
361         // first try capabilitiesOverride for type Id
362         int targetCapabilities = type.getCapabilitiesOverride();
363         // then try capabilitiesId
364         if (targetCapabilities < 0 && capabilitiesId != null && isCapabilitiesInDatabase(capabilitiesId.intValue())) {
365             targetCapabilities = capabilitiesId.intValue();
366         }
367         // then try capabilities for typeId
368         if (targetCapabilities < 0) {
369             targetCapabilities = type.getCapabilities();
370         }
371         // fallback to default capabilities 0 (so at least something may work..)
372         if (targetCapabilities < 0) {
373             targetCapabilities = 0;
374         }
375         return getCapabilities(targetCapabilities);
376     }
377
378     private static final String REQUEST_DEVELOPERS_TO_UPDATE = " => Please request developers to update the database!";
379
380     /**
381      * Log a message indicating that 'type' is not in database.
382      *
383      * @param type
384      */
385     public void logTypeNotInDatabase(int type) {
386         logger.warn("The shade 'type:{}' is not in the database!{}", type, REQUEST_DEVELOPERS_TO_UPDATE);
387     }
388
389     /**
390      * Log a message indicating that 'capabilities' is not in database.
391      *
392      * @param capabilities
393      */
394     public void logCapabilitiesNotInDatabase(int type, int capabilities) {
395         logger.warn("The 'capabilities:{}' for shade 'type:{}' are not in the database!{}", capabilities, type,
396                 REQUEST_DEVELOPERS_TO_UPDATE);
397     }
398
399     /**
400      * Log a message indicating the type's capabilities and the passed capabilities are not equal.
401      *
402      * @param type
403      * @param capabilities
404      */
405     public void logCapabilitiesMismatch(int type, int capabilities) {
406         logger.warn("The 'capabilities:{}' reported by shade 'type:{}' don't match the database!{}", capabilities, type,
407                 REQUEST_DEVELOPERS_TO_UPDATE);
408     }
409
410     /**
411      * Log a message indicating that a shade's secondary/vanes support, as observed via its actual JSON payload, does
412      * not match the expected value as declared in its 'type' and 'capabilities'.
413      *
414      * @param propertyKey
415      * @param type
416      * @param capabilities
417      * @param propertyValue
418      */
419     public void logPropertyMismatch(String propertyKey, int type, int capabilities, boolean propertyValue) {
420         logger.warn(
421                 "The '{}:{}' property actually reported by shade 'type:{}' is different "
422                         + "than expected from its 'capabilities:{}' in the database!{}",
423                 propertyKey, propertyValue, type, capabilities, REQUEST_DEVELOPERS_TO_UPDATE);
424     }
425 }