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