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