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