12
12
*/
13
13
package org .openhab .binding .mqtt .homeassistant .internal .component ;
14
14
15
+ import java .math .BigDecimal ;
16
+ import java .util .List ;
17
+
15
18
import org .eclipse .jdt .annotation .NonNullByDefault ;
16
19
import org .eclipse .jdt .annotation .Nullable ;
20
+ import org .openhab .binding .mqtt .generic .ChannelStateUpdateListener ;
17
21
import org .openhab .binding .mqtt .generic .values .OnOffValue ;
22
+ import org .openhab .binding .mqtt .generic .values .PercentageValue ;
23
+ import org .openhab .binding .mqtt .generic .values .TextValue ;
24
+ import org .openhab .binding .mqtt .homeassistant .internal .ComponentChannel ;
18
25
import org .openhab .binding .mqtt .homeassistant .internal .ComponentChannelType ;
19
26
import org .openhab .binding .mqtt .homeassistant .internal .config .dto .AbstractChannelConfiguration ;
27
+ import org .openhab .core .library .types .OnOffType ;
28
+ import org .openhab .core .library .types .PercentType ;
29
+ import org .openhab .core .thing .ChannelUID ;
30
+ import org .openhab .core .types .Command ;
31
+ import org .openhab .core .types .State ;
32
+ import org .openhab .core .types .UnDefType ;
20
33
21
34
import com .google .gson .annotations .SerializedName ;
22
35
28
41
* @author David Graeff - Initial contribution
29
42
*/
30
43
@ NonNullByDefault
31
- public class Fan extends AbstractComponent <Fan .ChannelConfiguration > {
32
- public static final String SWITCH_CHANNEL_ID = "fan" ; // Randomly chosen channel "ID"
44
+ public class Fan extends AbstractComponent <Fan .ChannelConfiguration > implements ChannelStateUpdateListener {
45
+ public static final String SWITCH_CHANNEL_ID = "fan" ;
46
+ public static final String SPEED_CHANNEL_ID = "speed" ;
47
+ public static final String PRESET_MODE_CHANNEL_ID = "preset_mode" ;
48
+ public static final String OSCILLATION_CHANNEL_ID = "oscillation" ;
49
+ public static final String DIRECTION_CHANNEL_ID = "direction" ;
33
50
34
51
/**
35
52
* Configuration class for MQTT component
@@ -45,21 +62,168 @@ static class ChannelConfiguration extends AbstractChannelConfiguration {
45
62
protected @ Nullable String commandTemplate ;
46
63
@ SerializedName ("command_topic" )
47
64
protected String commandTopic = "" ;
48
- @ SerializedName ("payload_on" )
49
- protected String payloadOn = "ON" ;
65
+ @ SerializedName ("direction_command_template" )
66
+ protected @ Nullable String directionCommandTemplate ;
67
+ @ SerializedName ("direction_command_topic" )
68
+ protected @ Nullable String directionCommandTopic ;
69
+ @ SerializedName ("direction_state_topic" )
70
+ protected @ Nullable String directionStateTopic ;
71
+ @ SerializedName ("direction_value_template" )
72
+ protected @ Nullable String directionValueTemplate ;
73
+ @ SerializedName ("oscillation_command_template" )
74
+ protected @ Nullable String oscillationCommandTemplate ;
75
+ @ SerializedName ("oscillation_command_topic" )
76
+ protected @ Nullable String oscillationCommandTopic ;
77
+ @ SerializedName ("oscillation_state_topic" )
78
+ protected @ Nullable String oscillationStateTopic ;
79
+ @ SerializedName ("oscillation_value_template" )
80
+ protected @ Nullable String oscillationValueTemplate ;
81
+ @ SerializedName ("payload_oscillation_off" )
82
+ protected String payloadOscillationOff = "oscillate_off" ;
83
+ @ SerializedName ("payload_oscillation_on" )
84
+ protected String payloadOscillationOn = "oscillate_on" ;
50
85
@ SerializedName ("payload_off" )
51
86
protected String payloadOff = "OFF" ;
87
+ @ SerializedName ("payload_on" )
88
+ protected String payloadOn = "ON" ;
89
+ @ SerializedName ("payload_reset_percentage" )
90
+ protected String payloadResetPercentage = "None" ;
91
+ @ SerializedName ("payload_reset_preset_mode" )
92
+ protected String payloadResetPresetMode = "None" ;
93
+ @ SerializedName ("percentage_command_template" )
94
+ protected @ Nullable String percentageCommandTemplate ;
95
+ @ SerializedName ("percentage_command_topic" )
96
+ protected @ Nullable String percentageCommandTopic ;
97
+ @ SerializedName ("percentage_state_topic" )
98
+ protected @ Nullable String percentageStateTopic ;
99
+ @ SerializedName ("percentage_value_template" )
100
+ protected @ Nullable String percentageValueTemplate ;
101
+ @ SerializedName ("preset_mode_command_template" )
102
+ protected @ Nullable String presetModeCommandTemplate ;
103
+ @ SerializedName ("preset_mode_command_topic" )
104
+ protected @ Nullable String presetModeCommandTopic ;
105
+ @ SerializedName ("preset_mode_state_topic" )
106
+ protected @ Nullable String presetModeStateTopic ;
107
+ @ SerializedName ("preset_mode_value_template" )
108
+ protected @ Nullable String presetModeValueTemplate ;
109
+ @ SerializedName ("preset_modes" )
110
+ protected @ Nullable List <String > presetModes ;
111
+ @ SerializedName ("speed_range_max" )
112
+ protected int speedRangeMax = 100 ;
113
+ @ SerializedName ("speed_range_min" )
114
+ protected int speedRangeMin = 1 ;
52
115
}
53
116
117
+ private final OnOffValue onOffValue ;
118
+ private final PercentageValue speedValue ;
119
+ private State rawSpeedState ;
120
+ private final ComponentChannel onOffChannel ;
121
+ private final ChannelStateUpdateListener channelStateUpdateListener ;
122
+
54
123
public Fan (ComponentFactory .ComponentConfiguration componentConfiguration , boolean newStyleChannels ) {
55
124
super (componentConfiguration , ChannelConfiguration .class , newStyleChannels );
125
+ this .channelStateUpdateListener = componentConfiguration .getUpdateListener ();
56
126
57
- OnOffValue value = new OnOffValue (channelConfiguration .payloadOn , channelConfiguration .payloadOff );
58
- buildChannel (SWITCH_CHANNEL_ID , ComponentChannelType .SWITCH , value , getName (),
59
- componentConfiguration .getUpdateListener ())
127
+ onOffValue = new OnOffValue (channelConfiguration .payloadOn , channelConfiguration .payloadOff );
128
+ ChannelStateUpdateListener onOffListener = channelConfiguration .percentageCommandTopic == null
129
+ ? componentConfiguration .getUpdateListener ()
130
+ : this ;
131
+ onOffChannel = buildChannel (SWITCH_CHANNEL_ID , ComponentChannelType .SWITCH , onOffValue , "On/Off State" ,
132
+ onOffListener )
60
133
.stateTopic (channelConfiguration .stateTopic , channelConfiguration .getValueTemplate ())
61
134
.commandTopic (channelConfiguration .commandTopic , channelConfiguration .isRetain (),
62
135
channelConfiguration .getQos (), channelConfiguration .commandTemplate )
63
- .build ();
136
+ .build (channelConfiguration .percentageCommandTopic == null );
137
+
138
+ rawSpeedState = UnDefType .NULL ;
139
+
140
+ int speeds = Math .min (channelConfiguration .speedRangeMax , 100 ) - Math .max (channelConfiguration .speedRangeMin , 1 )
141
+ + 1 ;
142
+ speedValue = new PercentageValue (BigDecimal .ZERO , BigDecimal .valueOf (100 ), BigDecimal .valueOf (100.0d / speeds ),
143
+ channelConfiguration .payloadOn , channelConfiguration .payloadOff );
144
+
145
+ if (channelConfiguration .percentageCommandTopic != null ) {
146
+ hiddenChannels .add (onOffChannel );
147
+ buildChannel (SPEED_CHANNEL_ID , ComponentChannelType .DIMMER , speedValue , "Speed" , this )
148
+ .stateTopic (channelConfiguration .percentageStateTopic , channelConfiguration .percentageValueTemplate )
149
+ .commandTopic (channelConfiguration .percentageCommandTopic , channelConfiguration .isRetain (),
150
+ channelConfiguration .getQos (), channelConfiguration .percentageCommandTemplate )
151
+ .commandFilter (this ::handlePercentageCommand ).build ();
152
+ }
153
+
154
+ List <String > presetModes = channelConfiguration .presetModes ;
155
+ if (presetModes != null ) {
156
+ TextValue presetModeValue = new TextValue (presetModes .toArray (new String [0 ]));
157
+ presetModeValue .setNullValue (channelConfiguration .payloadResetPresetMode );
158
+ buildChannel (PRESET_MODE_CHANNEL_ID , ComponentChannelType .STRING , presetModeValue , "Preset Mode" ,
159
+ componentConfiguration .getUpdateListener ())
160
+ .stateTopic (channelConfiguration .presetModeStateTopic , channelConfiguration .presetModeValueTemplate )
161
+ .commandTopic (channelConfiguration .presetModeCommandTopic , channelConfiguration .isRetain (),
162
+ channelConfiguration .getQos (), channelConfiguration .presetModeCommandTemplate )
163
+ .build ();
164
+ }
165
+
166
+ if (channelConfiguration .oscillationCommandTopic != null ) {
167
+ OnOffValue oscillationValue = new OnOffValue (channelConfiguration .payloadOscillationOn ,
168
+ channelConfiguration .payloadOscillationOff );
169
+ buildChannel (OSCILLATION_CHANNEL_ID , ComponentChannelType .SWITCH , oscillationValue , "Oscillation" ,
170
+ componentConfiguration .getUpdateListener ())
171
+ .stateTopic (channelConfiguration .oscillationStateTopic ,
172
+ channelConfiguration .oscillationValueTemplate )
173
+ .commandTopic (channelConfiguration .oscillationCommandTopic , channelConfiguration .isRetain (),
174
+ channelConfiguration .getQos (), channelConfiguration .oscillationCommandTemplate )
175
+ .build ();
176
+ }
177
+
178
+ if (channelConfiguration .directionCommandTopic != null ) {
179
+ TextValue directionValue = new TextValue (new String [] { "forward" , "backward" });
180
+ buildChannel (DIRECTION_CHANNEL_ID , ComponentChannelType .STRING , directionValue , "Direction" ,
181
+ componentConfiguration .getUpdateListener ())
182
+ .stateTopic (channelConfiguration .directionStateTopic , channelConfiguration .directionValueTemplate )
183
+ .commandTopic (channelConfiguration .directionCommandTopic , channelConfiguration .isRetain (),
184
+ channelConfiguration .getQos (), channelConfiguration .directionCommandTemplate )
185
+ .build ();
186
+ }
187
+ }
188
+
189
+ private boolean handlePercentageCommand (Command command ) {
190
+ // ON/OFF go to the regular command topic, not the percentage topic
191
+ if (command .equals (OnOffType .ON ) || command .equals (OnOffType .OFF )) {
192
+ onOffChannel .getState ().publishValue (command );
193
+ return false ;
194
+ }
195
+ return true ;
196
+ }
197
+
198
+ @ Override
199
+ public void updateChannelState (ChannelUID channel , State state ) {
200
+ if (channel .getIdWithoutGroup ().equals (SWITCH_CHANNEL_ID )) {
201
+ if (rawSpeedState instanceof UnDefType && state .equals (OnOffType .ON )) {
202
+ // Assume full on if we don't yet know the actual speed
203
+ state = PercentType .HUNDRED ;
204
+ } else if (state .equals (OnOffType .OFF )) {
205
+ state = PercentType .ZERO ;
206
+ } else {
207
+ state = rawSpeedState ;
208
+ }
209
+ } else if (channel .getIdWithoutGroup ().equals (SPEED_CHANNEL_ID )) {
210
+ rawSpeedState = state ;
211
+ if (onOffValue .getChannelState ().equals (OnOffType .OFF )) {
212
+ // Don't pass on percentage values while the fan is off
213
+ state = PercentType .ZERO ;
214
+ }
215
+ }
216
+ speedValue .update (state );
217
+ channelStateUpdateListener .updateChannelState (buildChannelUID (SPEED_CHANNEL_ID ), state );
218
+ }
219
+
220
+ @ Override
221
+ public void postChannelCommand (ChannelUID channelUID , Command value ) {
222
+ throw new UnsupportedOperationException ();
223
+ }
224
+
225
+ @ Override
226
+ public void triggerChannel (ChannelUID channelUID , String eventPayload ) {
227
+ throw new UnsupportedOperationException ();
64
228
}
65
229
}
0 commit comments