Skip to content

Commit 5d89c9a

Browse files
authored
[mqtt.homeassistant] Implement WaterHeater (openhab#17859)
* [mqtt.homeassistant] Implement WaterHeater Signed-off-by: Cody Cutrer <[email protected]>
1 parent f733c85 commit 5d89c9a

File tree

16 files changed

+455
-58
lines changed

16 files changed

+455
-58
lines changed

bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/generic/internal/MqttThingHandlerFactory.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
2323
import org.openhab.binding.mqtt.homeassistant.internal.HomeAssistantJinjaFunctionLibrary;
2424
import org.openhab.binding.mqtt.homeassistant.internal.handler.HomeAssistantThingHandler;
25+
import org.openhab.core.i18n.UnitProvider;
2526
import org.openhab.core.thing.Thing;
2627
import org.openhab.core.thing.ThingTypeUID;
2728
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
@@ -47,17 +48,19 @@ public class MqttThingHandlerFactory extends BaseThingHandlerFactory {
4748
private final MqttChannelStateDescriptionProvider stateDescriptionProvider;
4849
private final ChannelTypeRegistry channelTypeRegistry;
4950
private final Jinjava jinjava = new Jinjava();
51+
private final UnitProvider unitProvider;
5052

5153
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream
5254
.of(MqttBindingConstants.HOMEASSISTANT_MQTT_THING).collect(Collectors.toSet());
5355

5456
@Activate
5557
public MqttThingHandlerFactory(final @Reference MqttChannelTypeProvider typeProvider,
5658
final @Reference MqttChannelStateDescriptionProvider stateDescriptionProvider,
57-
final @Reference ChannelTypeRegistry channelTypeRegistry) {
59+
final @Reference ChannelTypeRegistry channelTypeRegistry, final @Reference UnitProvider unitProvider) {
5860
this.typeProvider = typeProvider;
5961
this.stateDescriptionProvider = stateDescriptionProvider;
6062
this.channelTypeRegistry = channelTypeRegistry;
63+
this.unitProvider = unitProvider;
6164

6265
HomeAssistantJinjaFunctionLibrary.register(jinjava.getGlobalContext());
6366
}
@@ -78,7 +81,7 @@ private boolean isHomeassistantDynamicType(ThingTypeUID thingTypeUID) {
7881

7982
if (supportsThingType(thingTypeUID)) {
8083
return new HomeAssistantThingHandler(thing, typeProvider, stateDescriptionProvider, channelTypeRegistry,
81-
jinjava, 10000, 2000);
84+
jinjava, unitProvider, 10000, 2000);
8285
}
8386
return null;
8487
}

bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentChannelType.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ public enum ComponentChannelType {
3333
SWITCH("ha-switch"),
3434
TRIGGER("ha-trigger"),
3535
HUMIDITY("ha-humidity"),
36-
GPS_ACCURACY("ha-gps-accuracy");
36+
GPS_ACCURACY("ha-gps-accuracy"),
37+
TEMPERATURE("ha-temperature");
3738

3839
final ChannelTypeUID channelTypeUID;
3940

bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/DiscoverComponents.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.openhab.binding.mqtt.homeassistant.internal.component.ComponentFactory;
3131
import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
3232
import org.openhab.binding.mqtt.homeassistant.internal.exception.UnsupportedComponentException;
33+
import org.openhab.core.i18n.UnitProvider;
3334
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
3435
import org.openhab.core.io.transport.mqtt.MqttMessageSubscriber;
3536
import org.openhab.core.thing.ThingUID;
@@ -57,6 +58,7 @@ public class DiscoverComponents implements MqttMessageSubscriber {
5758
protected final CompletableFuture<@Nullable Void> discoverFinishedFuture = new CompletableFuture<>();
5859
private final Gson gson;
5960
private final Jinjava jinjava;
61+
private final UnitProvider unitProvider;
6062

6163
private @Nullable ScheduledFuture<?> stopDiscoveryFuture;
6264
private WeakReference<@Nullable MqttBrokerConnection> connectionRef = new WeakReference<>(null);
@@ -82,12 +84,13 @@ public static interface ComponentDiscovered {
8284
*/
8385
public DiscoverComponents(ThingUID thingUID, ScheduledExecutorService scheduler,
8486
ChannelStateUpdateListener channelStateUpdateListener, AvailabilityTracker tracker, Gson gson,
85-
Jinjava jinjava, boolean newStyleChannels) {
87+
Jinjava jinjava, UnitProvider unitProvider, boolean newStyleChannels) {
8688
this.thingUID = thingUID;
8789
this.scheduler = scheduler;
8890
this.updateListener = channelStateUpdateListener;
8991
this.gson = gson;
9092
this.jinjava = jinjava;
93+
this.unitProvider = unitProvider;
9194
this.tracker = tracker;
9295
this.newStyleChannels = newStyleChannels;
9396
}
@@ -105,7 +108,7 @@ public void processMessage(String topic, byte[] payload) {
105108
if (config.length() > 0) {
106109
try {
107110
component = ComponentFactory.createComponent(thingUID, haID, config, updateListener, tracker, scheduler,
108-
gson, jinjava, newStyleChannels);
111+
gson, jinjava, unitProvider, newStyleChannels);
109112
component.setConfigSeen();
110113

111114
logger.trace("Found HomeAssistant component {}", haID);
@@ -119,8 +122,6 @@ public void processMessage(String topic, byte[] payload) {
119122
} catch (ConfigurationException e) {
120123
logger.warn("HomeAssistant discover error: invalid configuration of thing {} component {}: {}",
121124
haID.objectID, haID.component, e.getMessage());
122-
} catch (Exception e) {
123-
logger.warn("HomeAssistant discover error: {}", e.getMessage());
124125
}
125126
} else {
126127
if (discoveredListener != null) {

bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/AbstractComponent.java

+30
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
*/
1313
package org.openhab.binding.mqtt.homeassistant.internal.component;
1414

15+
import java.math.BigDecimal;
1516
import java.util.ArrayList;
1617
import java.util.List;
1718
import java.util.Map;
@@ -21,6 +22,9 @@
2122
import java.util.concurrent.ScheduledExecutorService;
2223
import java.util.stream.Stream;
2324

25+
import javax.measure.Unit;
26+
import javax.measure.quantity.Temperature;
27+
2428
import org.eclipse.jdt.annotation.NonNullByDefault;
2529
import org.eclipse.jdt.annotation.Nullable;
2630
import org.openhab.binding.mqtt.generic.AvailabilityTracker;
@@ -40,6 +44,8 @@
4044
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AvailabilityMode;
4145
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.Device;
4246
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
47+
import org.openhab.core.library.unit.ImperialUnits;
48+
import org.openhab.core.library.unit.SIUnits;
4349
import org.openhab.core.thing.Channel;
4450
import org.openhab.core.thing.ChannelUID;
4551
import org.openhab.core.thing.binding.generic.ChannelTransformation;
@@ -53,6 +59,7 @@
5359
import org.openhab.core.types.StateDescription;
5460

5561
import com.google.gson.Gson;
62+
import com.google.gson.annotations.SerializedName;
5663
import com.hubspot.jinjava.Jinjava;
5764

5865
/**
@@ -64,6 +71,29 @@
6471
*/
6572
@NonNullByDefault
6673
public abstract class AbstractComponent<C extends AbstractChannelConfiguration> {
74+
public enum TemperatureUnit {
75+
@SerializedName("C")
76+
CELSIUS(SIUnits.CELSIUS, new BigDecimal("0.1")),
77+
@SerializedName("F")
78+
FAHRENHEIT(ImperialUnits.FAHRENHEIT, BigDecimal.ONE);
79+
80+
private final Unit<Temperature> unit;
81+
private final BigDecimal defaultPrecision;
82+
83+
TemperatureUnit(Unit<Temperature> unit, BigDecimal defaultPrecision) {
84+
this.unit = unit;
85+
this.defaultPrecision = defaultPrecision;
86+
}
87+
88+
public Unit<Temperature> getUnit() {
89+
return unit;
90+
}
91+
92+
public BigDecimal getDefaultPrecision() {
93+
return defaultPrecision;
94+
}
95+
}
96+
6797
public static final String JSON_ATTRIBUTES_CHANNEL_ID = "json-attributes";
6898

6999
// Component location fields

bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Climate.java

+18-36
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import java.util.List;
1818
import java.util.function.Predicate;
1919

20-
import javax.measure.Unit;
2120
import javax.measure.quantity.Temperature;
2221

2322
import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -32,7 +31,6 @@
3231
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
3332
import org.openhab.core.library.types.StringType;
3433
import org.openhab.core.library.unit.ImperialUnits;
35-
import org.openhab.core.library.unit.SIUnits;
3634
import org.openhab.core.library.unit.Units;
3735
import org.openhab.core.types.Command;
3836
import org.openhab.core.types.State;
@@ -69,29 +67,6 @@ public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
6967
public static final String TEMPERATURE_LOW_CH_ID_DEPRECATED = "temperatureLow";
7068
public static final String POWER_CH_ID = "power";
7169

72-
public enum TemperatureUnit {
73-
@SerializedName("C")
74-
CELSIUS(SIUnits.CELSIUS, new BigDecimal("0.1")),
75-
@SerializedName("F")
76-
FAHRENHEIT(ImperialUnits.FAHRENHEIT, BigDecimal.ONE);
77-
78-
private final Unit<Temperature> unit;
79-
private final BigDecimal defaultPrecision;
80-
81-
TemperatureUnit(Unit<Temperature> unit, BigDecimal defaultPrecision) {
82-
this.unit = unit;
83-
this.defaultPrecision = defaultPrecision;
84-
}
85-
86-
public Unit<Temperature> getUnit() {
87-
return unit;
88-
}
89-
90-
public BigDecimal getDefaultPrecision() {
91-
return defaultPrecision;
92-
}
93-
}
94-
9570
private static final String ACTION_OFF = "off";
9671
private static final State ACTION_OFF_STATE = new StringType(ACTION_OFF);
9772
private static final List<String> ACTION_MODES = List.of(ACTION_OFF, "heating", "cooling", "drying", "idle", "fan");
@@ -241,7 +216,7 @@ static class ChannelConfiguration extends AbstractChannelConfiguration {
241216
@SerializedName("min_temp")
242217
protected @Nullable BigDecimal minTemp;
243218
@SerializedName("temperature_unit")
244-
protected TemperatureUnit temperatureUnit = TemperatureUnit.CELSIUS; // System unit by default
219+
protected @Nullable TemperatureUnit temperatureUnit;
245220
@SerializedName("temp_step")
246221
protected BigDecimal tempStep = BigDecimal.ONE;
247222
protected @Nullable BigDecimal precision;
@@ -252,8 +227,16 @@ static class ChannelConfiguration extends AbstractChannelConfiguration {
252227
public Climate(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
253228
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels);
254229

230+
TemperatureUnit temperatureUnit = channelConfiguration.temperatureUnit;
231+
if (channelConfiguration.temperatureUnit == null) {
232+
if (ImperialUnits.FAHRENHEIT.equals(componentConfiguration.getUnitProvider().getUnit(Temperature.class))) {
233+
temperatureUnit = TemperatureUnit.FAHRENHEIT;
234+
} else {
235+
temperatureUnit = TemperatureUnit.CELSIUS;
236+
}
237+
}
255238
BigDecimal precision = channelConfiguration.precision != null ? channelConfiguration.precision
256-
: channelConfiguration.temperatureUnit.getDefaultPrecision();
239+
: temperatureUnit.getDefaultPrecision();
257240
final ChannelStateUpdateListener updateListener = componentConfiguration.getUpdateListener();
258241

259242
ComponentChannel actionChannel = buildOptionalChannel(ACTION_CH_ID, ComponentChannelType.STRING,
@@ -277,9 +260,8 @@ ComponentChannelType.SWITCH, new OnOffValue(), updateListener, null,
277260
null, channelConfiguration.currentHumidityTemplate, channelConfiguration.currentHumidityTopic, null);
278261

279262
buildOptionalChannel(newStyleChannels ? CURRENT_TEMPERATURE_CH_ID : CURRENT_TEMPERATURE_CH_ID_DEPRECATED,
280-
ComponentChannelType.NUMBER,
281-
new NumberValue(null, null, precision, channelConfiguration.temperatureUnit.getUnit()), updateListener,
282-
null, null, channelConfiguration.currentTemperatureTemplate,
263+
ComponentChannelType.TEMPERATURE, new NumberValue(null, null, precision, temperatureUnit.getUnit()),
264+
updateListener, null, null, channelConfiguration.currentTemperatureTemplate,
283265
channelConfiguration.currentTemperatureTopic, commandFilter);
284266

285267
buildOptionalChannel(newStyleChannels ? FAN_MODE_CH_ID : FAN_MODE_CH_ID_DEPRECATED, ComponentChannelType.STRING,
@@ -317,25 +299,25 @@ ComponentChannelType.SWITCH, new OnOffValue(), updateListener, null,
317299
channelConfiguration.targetHumidityCommandTopic, channelConfiguration.targetHumidityStateTemplate,
318300
channelConfiguration.targetHumidityStateTopic, commandFilter);
319301

320-
buildOptionalChannel(TEMPERATURE_CH_ID, ComponentChannelType.NUMBER,
302+
buildOptionalChannel(TEMPERATURE_CH_ID, ComponentChannelType.TEMPERATURE,
321303
new NumberValue(channelConfiguration.minTemp, channelConfiguration.maxTemp,
322-
channelConfiguration.tempStep, channelConfiguration.temperatureUnit.getUnit()),
304+
channelConfiguration.tempStep, temperatureUnit.getUnit()),
323305
updateListener, channelConfiguration.temperatureCommandTemplate,
324306
channelConfiguration.temperatureCommandTopic, channelConfiguration.temperatureStateTemplate,
325307
channelConfiguration.temperatureStateTopic, commandFilter);
326308

327309
buildOptionalChannel(newStyleChannels ? TEMPERATURE_HIGH_CH_ID : TEMPERATURE_HIGH_CH_ID_DEPRECATED,
328-
ComponentChannelType.NUMBER,
310+
ComponentChannelType.TEMPERATURE,
329311
new NumberValue(channelConfiguration.minTemp, channelConfiguration.maxTemp,
330-
channelConfiguration.tempStep, channelConfiguration.temperatureUnit.getUnit()),
312+
channelConfiguration.tempStep, temperatureUnit.getUnit()),
331313
updateListener, channelConfiguration.temperatureHighCommandTemplate,
332314
channelConfiguration.temperatureHighCommandTopic, channelConfiguration.temperatureHighStateTemplate,
333315
channelConfiguration.temperatureHighStateTopic, commandFilter);
334316

335317
buildOptionalChannel(newStyleChannels ? TEMPERATURE_LOW_CH_ID : TEMPERATURE_LOW_CH_ID_DEPRECATED,
336-
ComponentChannelType.NUMBER,
318+
ComponentChannelType.TEMPERATURE,
337319
new NumberValue(channelConfiguration.minTemp, channelConfiguration.maxTemp,
338-
channelConfiguration.tempStep, channelConfiguration.temperatureUnit.getUnit()),
320+
channelConfiguration.tempStep, temperatureUnit.getUnit()),
339321
updateListener, channelConfiguration.temperatureLowCommandTemplate,
340322
channelConfiguration.temperatureLowCommandTopic, channelConfiguration.temperatureLowStateTemplate,
341323
channelConfiguration.temperatureLowStateTopic, commandFilter);

bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/ComponentFactory.java

+13-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
2222
import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
2323
import org.openhab.binding.mqtt.homeassistant.internal.exception.UnsupportedComponentException;
24+
import org.openhab.core.i18n.UnitProvider;
2425
import org.openhab.core.thing.ThingUID;
2526

2627
import com.google.gson.Gson;
@@ -47,9 +48,10 @@ public class ComponentFactory {
4748
*/
4849
public static AbstractComponent<?> createComponent(ThingUID thingUID, HaID haID, String channelConfigurationJSON,
4950
ChannelStateUpdateListener updateListener, AvailabilityTracker tracker, ScheduledExecutorService scheduler,
50-
Gson gson, Jinjava jinjava, boolean newStyleChannels) throws ConfigurationException {
51+
Gson gson, Jinjava jinjava, UnitProvider unitProvider, boolean newStyleChannels)
52+
throws ConfigurationException {
5153
ComponentConfiguration componentConfiguration = new ComponentConfiguration(thingUID, haID,
52-
channelConfigurationJSON, gson, jinjava, updateListener, tracker, scheduler);
54+
channelConfigurationJSON, gson, jinjava, updateListener, tracker, scheduler, unitProvider);
5355
switch (haID.component) {
5456
case "alarm_control_panel":
5557
return new AlarmControlPanel(componentConfiguration, newStyleChannels);
@@ -97,6 +99,8 @@ public static AbstractComponent<?> createComponent(ThingUID thingUID, HaID haID,
9799
return new Vacuum(componentConfiguration, newStyleChannels);
98100
case "valve":
99101
return new Valve(componentConfiguration, newStyleChannels);
102+
case "water_heater":
103+
return new WaterHeater(componentConfiguration, newStyleChannels);
100104
default:
101105
throw new UnsupportedComponentException("Component '" + haID + "' is unsupported!");
102106
}
@@ -111,6 +115,7 @@ protected static class ComponentConfiguration {
111115
private final Gson gson;
112116
private final Jinjava jinjava;
113117
private final ScheduledExecutorService scheduler;
118+
private final UnitProvider unitProvider;
114119

115120
/**
116121
* Provide a thingUID and HomeAssistant topic ID to determine the channel group UID and type.
@@ -122,7 +127,7 @@ protected static class ComponentConfiguration {
122127
*/
123128
protected ComponentConfiguration(ThingUID thingUID, HaID haID, String configJSON, Gson gson, Jinjava jinjava,
124129
ChannelStateUpdateListener updateListener, AvailabilityTracker tracker,
125-
ScheduledExecutorService scheduler) {
130+
ScheduledExecutorService scheduler, UnitProvider unitProvider) {
126131
this.thingUID = thingUID;
127132
this.haID = haID;
128133
this.configJSON = configJSON;
@@ -131,6 +136,7 @@ protected ComponentConfiguration(ThingUID thingUID, HaID haID, String configJSON
131136
this.updateListener = updateListener;
132137
this.tracker = tracker;
133138
this.scheduler = scheduler;
139+
this.unitProvider = unitProvider;
134140
}
135141

136142
public ThingUID getThingUID() {
@@ -157,6 +163,10 @@ public Jinjava getJinjava() {
157163
return jinjava;
158164
}
159165

166+
public UnitProvider getUnitProvider() {
167+
return unitProvider;
168+
}
169+
160170
public AvailabilityTracker getTracker() {
161171
return tracker;
162172
}

0 commit comments

Comments
 (0)