mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[mqtt.homeassistant] Implement WaterHeater (#17859)
* [mqtt.homeassistant] Implement WaterHeater Signed-off-by: Cody Cutrer <cody@cutrer.us> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
56c55a9604
commit
74329618b5
@ -22,6 +22,7 @@ import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.HomeAssistantJinjaFunctionLibrary;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.handler.HomeAssistantThingHandler;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
@ -47,6 +48,7 @@ public class MqttThingHandlerFactory extends BaseThingHandlerFactory {
|
||||
private final MqttChannelStateDescriptionProvider stateDescriptionProvider;
|
||||
private final ChannelTypeRegistry channelTypeRegistry;
|
||||
private final Jinjava jinjava = new Jinjava();
|
||||
private final UnitProvider unitProvider;
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream
|
||||
.of(MqttBindingConstants.HOMEASSISTANT_MQTT_THING).collect(Collectors.toSet());
|
||||
@ -54,10 +56,11 @@ public class MqttThingHandlerFactory extends BaseThingHandlerFactory {
|
||||
@Activate
|
||||
public MqttThingHandlerFactory(final @Reference MqttChannelTypeProvider typeProvider,
|
||||
final @Reference MqttChannelStateDescriptionProvider stateDescriptionProvider,
|
||||
final @Reference ChannelTypeRegistry channelTypeRegistry) {
|
||||
final @Reference ChannelTypeRegistry channelTypeRegistry, final @Reference UnitProvider unitProvider) {
|
||||
this.typeProvider = typeProvider;
|
||||
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||
this.channelTypeRegistry = channelTypeRegistry;
|
||||
this.unitProvider = unitProvider;
|
||||
|
||||
HomeAssistantJinjaFunctionLibrary.register(jinjava.getGlobalContext());
|
||||
}
|
||||
@ -78,7 +81,7 @@ public class MqttThingHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
if (supportsThingType(thingTypeUID)) {
|
||||
return new HomeAssistantThingHandler(thing, typeProvider, stateDescriptionProvider, channelTypeRegistry,
|
||||
jinjava, 10000, 2000);
|
||||
jinjava, unitProvider, 10000, 2000);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -33,7 +33,8 @@ public enum ComponentChannelType {
|
||||
SWITCH("ha-switch"),
|
||||
TRIGGER("ha-trigger"),
|
||||
HUMIDITY("ha-humidity"),
|
||||
GPS_ACCURACY("ha-gps-accuracy");
|
||||
GPS_ACCURACY("ha-gps-accuracy"),
|
||||
TEMPERATURE("ha-temperature");
|
||||
|
||||
final ChannelTypeUID channelTypeUID;
|
||||
|
||||
|
@ -30,6 +30,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.component.AbstractCompone
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.component.ComponentFactory;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.exception.UnsupportedComponentException;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
|
||||
import org.openhab.core.io.transport.mqtt.MqttMessageSubscriber;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
@ -57,6 +58,7 @@ public class DiscoverComponents implements MqttMessageSubscriber {
|
||||
protected final CompletableFuture<@Nullable Void> discoverFinishedFuture = new CompletableFuture<>();
|
||||
private final Gson gson;
|
||||
private final Jinjava jinjava;
|
||||
private final UnitProvider unitProvider;
|
||||
|
||||
private @Nullable ScheduledFuture<?> stopDiscoveryFuture;
|
||||
private WeakReference<@Nullable MqttBrokerConnection> connectionRef = new WeakReference<>(null);
|
||||
@ -82,12 +84,13 @@ public class DiscoverComponents implements MqttMessageSubscriber {
|
||||
*/
|
||||
public DiscoverComponents(ThingUID thingUID, ScheduledExecutorService scheduler,
|
||||
ChannelStateUpdateListener channelStateUpdateListener, AvailabilityTracker tracker, Gson gson,
|
||||
Jinjava jinjava, boolean newStyleChannels) {
|
||||
Jinjava jinjava, UnitProvider unitProvider, boolean newStyleChannels) {
|
||||
this.thingUID = thingUID;
|
||||
this.scheduler = scheduler;
|
||||
this.updateListener = channelStateUpdateListener;
|
||||
this.gson = gson;
|
||||
this.jinjava = jinjava;
|
||||
this.unitProvider = unitProvider;
|
||||
this.tracker = tracker;
|
||||
this.newStyleChannels = newStyleChannels;
|
||||
}
|
||||
@ -105,7 +108,7 @@ public class DiscoverComponents implements MqttMessageSubscriber {
|
||||
if (config.length() > 0) {
|
||||
try {
|
||||
component = ComponentFactory.createComponent(thingUID, haID, config, updateListener, tracker, scheduler,
|
||||
gson, jinjava, newStyleChannels);
|
||||
gson, jinjava, unitProvider, newStyleChannels);
|
||||
component.setConfigSeen();
|
||||
|
||||
logger.trace("Found HomeAssistant component {}", haID);
|
||||
@ -119,8 +122,6 @@ public class DiscoverComponents implements MqttMessageSubscriber {
|
||||
} catch (ConfigurationException e) {
|
||||
logger.warn("HomeAssistant discover error: invalid configuration of thing {} component {}: {}",
|
||||
haID.objectID, haID.component, e.getMessage());
|
||||
} catch (Exception e) {
|
||||
logger.warn("HomeAssistant discover error: {}", e.getMessage());
|
||||
}
|
||||
} else {
|
||||
if (discoveredListener != null) {
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
package org.openhab.binding.mqtt.homeassistant.internal.component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -21,6 +22,9 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.measure.Unit;
|
||||
import javax.measure.quantity.Temperature;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.mqtt.generic.AvailabilityTracker;
|
||||
@ -40,6 +44,8 @@ import org.openhab.binding.mqtt.homeassistant.internal.config.dto.Availability;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AvailabilityMode;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.Device;
|
||||
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.binding.generic.ChannelTransformation;
|
||||
@ -53,6 +59,7 @@ import org.openhab.core.types.CommandDescription;
|
||||
import org.openhab.core.types.StateDescription;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.hubspot.jinjava.Jinjava;
|
||||
|
||||
/**
|
||||
@ -64,6 +71,29 @@ import com.hubspot.jinjava.Jinjava;
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class AbstractComponent<C extends AbstractChannelConfiguration> {
|
||||
public enum TemperatureUnit {
|
||||
@SerializedName("C")
|
||||
CELSIUS(SIUnits.CELSIUS, new BigDecimal("0.1")),
|
||||
@SerializedName("F")
|
||||
FAHRENHEIT(ImperialUnits.FAHRENHEIT, BigDecimal.ONE);
|
||||
|
||||
private final Unit<Temperature> unit;
|
||||
private final BigDecimal defaultPrecision;
|
||||
|
||||
TemperatureUnit(Unit<Temperature> unit, BigDecimal defaultPrecision) {
|
||||
this.unit = unit;
|
||||
this.defaultPrecision = defaultPrecision;
|
||||
}
|
||||
|
||||
public Unit<Temperature> getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public BigDecimal getDefaultPrecision() {
|
||||
return defaultPrecision;
|
||||
}
|
||||
}
|
||||
|
||||
public static final String JSON_ATTRIBUTES_CHANNEL_ID = "json-attributes";
|
||||
|
||||
// Component location fields
|
||||
|
@ -17,7 +17,6 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.measure.Unit;
|
||||
import javax.measure.quantity.Temperature;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
@ -32,7 +31,6 @@ import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannelType;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
@ -69,29 +67,6 @@ public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
|
||||
public static final String TEMPERATURE_LOW_CH_ID_DEPRECATED = "temperatureLow";
|
||||
public static final String POWER_CH_ID = "power";
|
||||
|
||||
public enum TemperatureUnit {
|
||||
@SerializedName("C")
|
||||
CELSIUS(SIUnits.CELSIUS, new BigDecimal("0.1")),
|
||||
@SerializedName("F")
|
||||
FAHRENHEIT(ImperialUnits.FAHRENHEIT, BigDecimal.ONE);
|
||||
|
||||
private final Unit<Temperature> unit;
|
||||
private final BigDecimal defaultPrecision;
|
||||
|
||||
TemperatureUnit(Unit<Temperature> unit, BigDecimal defaultPrecision) {
|
||||
this.unit = unit;
|
||||
this.defaultPrecision = defaultPrecision;
|
||||
}
|
||||
|
||||
public Unit<Temperature> getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public BigDecimal getDefaultPrecision() {
|
||||
return defaultPrecision;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String ACTION_OFF = "off";
|
||||
private static final State ACTION_OFF_STATE = new StringType(ACTION_OFF);
|
||||
private static final List<String> ACTION_MODES = List.of(ACTION_OFF, "heating", "cooling", "drying", "idle", "fan");
|
||||
@ -241,7 +216,7 @@ public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
|
||||
@SerializedName("min_temp")
|
||||
protected @Nullable BigDecimal minTemp;
|
||||
@SerializedName("temperature_unit")
|
||||
protected TemperatureUnit temperatureUnit = TemperatureUnit.CELSIUS; // System unit by default
|
||||
protected @Nullable TemperatureUnit temperatureUnit;
|
||||
@SerializedName("temp_step")
|
||||
protected BigDecimal tempStep = BigDecimal.ONE;
|
||||
protected @Nullable BigDecimal precision;
|
||||
@ -252,8 +227,16 @@ public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
|
||||
public Climate(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
||||
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels);
|
||||
|
||||
TemperatureUnit temperatureUnit = channelConfiguration.temperatureUnit;
|
||||
if (channelConfiguration.temperatureUnit == null) {
|
||||
if (ImperialUnits.FAHRENHEIT.equals(componentConfiguration.getUnitProvider().getUnit(Temperature.class))) {
|
||||
temperatureUnit = TemperatureUnit.FAHRENHEIT;
|
||||
} else {
|
||||
temperatureUnit = TemperatureUnit.CELSIUS;
|
||||
}
|
||||
}
|
||||
BigDecimal precision = channelConfiguration.precision != null ? channelConfiguration.precision
|
||||
: channelConfiguration.temperatureUnit.getDefaultPrecision();
|
||||
: temperatureUnit.getDefaultPrecision();
|
||||
final ChannelStateUpdateListener updateListener = componentConfiguration.getUpdateListener();
|
||||
|
||||
ComponentChannel actionChannel = buildOptionalChannel(ACTION_CH_ID, ComponentChannelType.STRING,
|
||||
@ -277,9 +260,8 @@ public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
|
||||
null, channelConfiguration.currentHumidityTemplate, channelConfiguration.currentHumidityTopic, null);
|
||||
|
||||
buildOptionalChannel(newStyleChannels ? CURRENT_TEMPERATURE_CH_ID : CURRENT_TEMPERATURE_CH_ID_DEPRECATED,
|
||||
ComponentChannelType.NUMBER,
|
||||
new NumberValue(null, null, precision, channelConfiguration.temperatureUnit.getUnit()), updateListener,
|
||||
null, null, channelConfiguration.currentTemperatureTemplate,
|
||||
ComponentChannelType.TEMPERATURE, new NumberValue(null, null, precision, temperatureUnit.getUnit()),
|
||||
updateListener, null, null, channelConfiguration.currentTemperatureTemplate,
|
||||
channelConfiguration.currentTemperatureTopic, commandFilter);
|
||||
|
||||
buildOptionalChannel(newStyleChannels ? FAN_MODE_CH_ID : FAN_MODE_CH_ID_DEPRECATED, ComponentChannelType.STRING,
|
||||
@ -317,25 +299,25 @@ public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
|
||||
channelConfiguration.targetHumidityCommandTopic, channelConfiguration.targetHumidityStateTemplate,
|
||||
channelConfiguration.targetHumidityStateTopic, commandFilter);
|
||||
|
||||
buildOptionalChannel(TEMPERATURE_CH_ID, ComponentChannelType.NUMBER,
|
||||
buildOptionalChannel(TEMPERATURE_CH_ID, ComponentChannelType.TEMPERATURE,
|
||||
new NumberValue(channelConfiguration.minTemp, channelConfiguration.maxTemp,
|
||||
channelConfiguration.tempStep, channelConfiguration.temperatureUnit.getUnit()),
|
||||
channelConfiguration.tempStep, temperatureUnit.getUnit()),
|
||||
updateListener, channelConfiguration.temperatureCommandTemplate,
|
||||
channelConfiguration.temperatureCommandTopic, channelConfiguration.temperatureStateTemplate,
|
||||
channelConfiguration.temperatureStateTopic, commandFilter);
|
||||
|
||||
buildOptionalChannel(newStyleChannels ? TEMPERATURE_HIGH_CH_ID : TEMPERATURE_HIGH_CH_ID_DEPRECATED,
|
||||
ComponentChannelType.NUMBER,
|
||||
ComponentChannelType.TEMPERATURE,
|
||||
new NumberValue(channelConfiguration.minTemp, channelConfiguration.maxTemp,
|
||||
channelConfiguration.tempStep, channelConfiguration.temperatureUnit.getUnit()),
|
||||
channelConfiguration.tempStep, temperatureUnit.getUnit()),
|
||||
updateListener, channelConfiguration.temperatureHighCommandTemplate,
|
||||
channelConfiguration.temperatureHighCommandTopic, channelConfiguration.temperatureHighStateTemplate,
|
||||
channelConfiguration.temperatureHighStateTopic, commandFilter);
|
||||
|
||||
buildOptionalChannel(newStyleChannels ? TEMPERATURE_LOW_CH_ID : TEMPERATURE_LOW_CH_ID_DEPRECATED,
|
||||
ComponentChannelType.NUMBER,
|
||||
ComponentChannelType.TEMPERATURE,
|
||||
new NumberValue(channelConfiguration.minTemp, channelConfiguration.maxTemp,
|
||||
channelConfiguration.tempStep, channelConfiguration.temperatureUnit.getUnit()),
|
||||
channelConfiguration.tempStep, temperatureUnit.getUnit()),
|
||||
updateListener, channelConfiguration.temperatureLowCommandTemplate,
|
||||
channelConfiguration.temperatureLowCommandTopic, channelConfiguration.temperatureLowStateTemplate,
|
||||
channelConfiguration.temperatureLowStateTopic, commandFilter);
|
||||
|
@ -21,6 +21,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.HaID;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.exception.UnsupportedComponentException;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
@ -47,9 +48,10 @@ public class ComponentFactory {
|
||||
*/
|
||||
public static AbstractComponent<?> createComponent(ThingUID thingUID, HaID haID, String channelConfigurationJSON,
|
||||
ChannelStateUpdateListener updateListener, AvailabilityTracker tracker, ScheduledExecutorService scheduler,
|
||||
Gson gson, Jinjava jinjava, boolean newStyleChannels) throws ConfigurationException {
|
||||
Gson gson, Jinjava jinjava, UnitProvider unitProvider, boolean newStyleChannels)
|
||||
throws ConfigurationException {
|
||||
ComponentConfiguration componentConfiguration = new ComponentConfiguration(thingUID, haID,
|
||||
channelConfigurationJSON, gson, jinjava, updateListener, tracker, scheduler);
|
||||
channelConfigurationJSON, gson, jinjava, updateListener, tracker, scheduler, unitProvider);
|
||||
switch (haID.component) {
|
||||
case "alarm_control_panel":
|
||||
return new AlarmControlPanel(componentConfiguration, newStyleChannels);
|
||||
@ -97,6 +99,8 @@ public class ComponentFactory {
|
||||
return new Vacuum(componentConfiguration, newStyleChannels);
|
||||
case "valve":
|
||||
return new Valve(componentConfiguration, newStyleChannels);
|
||||
case "water_heater":
|
||||
return new WaterHeater(componentConfiguration, newStyleChannels);
|
||||
default:
|
||||
throw new UnsupportedComponentException("Component '" + haID + "' is unsupported!");
|
||||
}
|
||||
@ -111,6 +115,7 @@ public class ComponentFactory {
|
||||
private final Gson gson;
|
||||
private final Jinjava jinjava;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final UnitProvider unitProvider;
|
||||
|
||||
/**
|
||||
* Provide a thingUID and HomeAssistant topic ID to determine the channel group UID and type.
|
||||
@ -122,7 +127,7 @@ public class ComponentFactory {
|
||||
*/
|
||||
protected ComponentConfiguration(ThingUID thingUID, HaID haID, String configJSON, Gson gson, Jinjava jinjava,
|
||||
ChannelStateUpdateListener updateListener, AvailabilityTracker tracker,
|
||||
ScheduledExecutorService scheduler) {
|
||||
ScheduledExecutorService scheduler, UnitProvider unitProvider) {
|
||||
this.thingUID = thingUID;
|
||||
this.haID = haID;
|
||||
this.configJSON = configJSON;
|
||||
@ -131,6 +136,7 @@ public class ComponentFactory {
|
||||
this.updateListener = updateListener;
|
||||
this.tracker = tracker;
|
||||
this.scheduler = scheduler;
|
||||
this.unitProvider = unitProvider;
|
||||
}
|
||||
|
||||
public ThingUID getThingUID() {
|
||||
@ -157,6 +163,10 @@ public class ComponentFactory {
|
||||
return jinjava;
|
||||
}
|
||||
|
||||
public UnitProvider getUnitProvider() {
|
||||
return unitProvider;
|
||||
}
|
||||
|
||||
public AvailabilityTracker getTracker() {
|
||||
return tracker;
|
||||
}
|
||||
|
@ -0,0 +1,190 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.mqtt.homeassistant.internal.component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.measure.quantity.Temperature;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.mqtt.generic.values.NumberValue;
|
||||
import org.openhab.binding.mqtt.generic.values.OnOffValue;
|
||||
import org.openhab.binding.mqtt.generic.values.TextValue;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannelType;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* A MQTT Humidifier, following the https://www.home-assistant.io/integrations/water_heater.mqtt/ specification.
|
||||
*
|
||||
* @author Cody Cutrer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WaterHeater extends AbstractComponent<WaterHeater.ChannelConfiguration> {
|
||||
public static final String CURRENT_TEMPERATURE_CHANNEL_ID = "current-temperature";
|
||||
public static final String MODE_CHANNEL_ID = "mode";
|
||||
public static final String STATE_CHANNEL_ID = "state";
|
||||
public static final String TARGET_TEMPERATURE_CHANNEL_ID = "target-temperature";
|
||||
|
||||
public static final String PLATFORM_WATER_HEATER = "water_heater";
|
||||
|
||||
public static final String MODE_OFF = "off";
|
||||
public static final String MODE_ECO = "eco";
|
||||
public static final String MODE_ELECTRIC = "electric";
|
||||
public static final String MODE_GAS = "gas";
|
||||
public static final String MODE_HEAT_PUMP = "heat_pump";
|
||||
public static final String MODE_HIGH_DEMAND = "high_demand";
|
||||
public static final String MODE_PERFORMANCE = "performance";
|
||||
public static final List<String> DEFAULT_MODES = List.of(MODE_OFF, MODE_ECO, MODE_ELECTRIC, MODE_GAS,
|
||||
MODE_HEAT_PUMP, MODE_HIGH_DEMAND, MODE_PERFORMANCE);
|
||||
|
||||
public static final String TEMPERATURE_UNIT_C = "C";
|
||||
public static final String TEMPERATURE_UNIT_F = "F";
|
||||
|
||||
/**
|
||||
* Configuration class for MQTT component
|
||||
*/
|
||||
static class ChannelConfiguration extends AbstractChannelConfiguration {
|
||||
ChannelConfiguration() {
|
||||
super("MQTT Humidifier");
|
||||
}
|
||||
|
||||
protected @Nullable Boolean optimistic;
|
||||
|
||||
@SerializedName("power_command_topic")
|
||||
protected @Nullable String powerCommandTopic;
|
||||
@SerializedName("power_command_template")
|
||||
protected @Nullable String powerCommandTemplate;
|
||||
@SerializedName("current_temperature_topic")
|
||||
protected @Nullable String currentTemperatureTopic;
|
||||
@SerializedName("current_temperature_template")
|
||||
protected @Nullable String currentTemperatureTemplate;
|
||||
@SerializedName("temperature_command_topic")
|
||||
protected @Nullable String temperatureCommandTopic;
|
||||
@SerializedName("temperature_command_template")
|
||||
protected @Nullable String temperatureCommandTemplate;
|
||||
@SerializedName("temperature_state_topic")
|
||||
protected @Nullable String temperatureStateTopic;
|
||||
@SerializedName("temperature_state_template")
|
||||
protected @Nullable String temperatureStateTemplate;
|
||||
@SerializedName("mode_command_topic")
|
||||
protected @Nullable String modeCommandTopic;
|
||||
@SerializedName("mode_command_template")
|
||||
protected @Nullable String modeCommandTemplate;
|
||||
@SerializedName("mode_state_topic")
|
||||
protected @Nullable String modeStateTopic;
|
||||
@SerializedName("mode_state_template")
|
||||
protected @Nullable String modeStateTemplate;
|
||||
|
||||
@SerializedName("device_class")
|
||||
protected @Nullable String deviceClass;
|
||||
protected String platform = "";
|
||||
|
||||
protected @Nullable Integer initial;
|
||||
@SerializedName("min_temp")
|
||||
protected @Nullable BigDecimal minTemp;
|
||||
@SerializedName("max_temp")
|
||||
protected @Nullable BigDecimal maxTemp;
|
||||
protected @Nullable BigDecimal precision;
|
||||
@SerializedName("temperature_unit")
|
||||
protected @Nullable TemperatureUnit temperatureUnit;
|
||||
|
||||
@SerializedName("payload_on")
|
||||
protected String payloadOn = "ON";
|
||||
@SerializedName("payload_off")
|
||||
protected String payloadOff = "OFF";
|
||||
protected List<String> modes = DEFAULT_MODES;
|
||||
}
|
||||
|
||||
public WaterHeater(ComponentFactory.ComponentConfiguration componentConfiguration, boolean newStyleChannels) {
|
||||
super(componentConfiguration, ChannelConfiguration.class, newStyleChannels);
|
||||
|
||||
if (!PLATFORM_WATER_HEATER.equals(channelConfiguration.platform)) {
|
||||
throw new ConfigurationException("platform must be " + PLATFORM_WATER_HEATER);
|
||||
}
|
||||
|
||||
TemperatureUnit temperatureUnit = channelConfiguration.temperatureUnit;
|
||||
if (channelConfiguration.temperatureUnit == null) {
|
||||
if (ImperialUnits.FAHRENHEIT.equals(componentConfiguration.getUnitProvider().getUnit(Temperature.class))) {
|
||||
temperatureUnit = TemperatureUnit.FAHRENHEIT;
|
||||
} else {
|
||||
temperatureUnit = TemperatureUnit.CELSIUS;
|
||||
}
|
||||
}
|
||||
BigDecimal precision = channelConfiguration.precision != null ? channelConfiguration.precision
|
||||
: temperatureUnit.getDefaultPrecision();
|
||||
|
||||
List<String> onStates = new ArrayList<>(channelConfiguration.modes);
|
||||
onStates.remove(MODE_OFF);
|
||||
|
||||
List<String> unsupportedModes = onStates.stream().filter(mode -> !DEFAULT_MODES.contains(mode))
|
||||
.collect(Collectors.toList());
|
||||
if (!unsupportedModes.isEmpty()) {
|
||||
throw new ConfigurationException("unsupported modes: " + unsupportedModes.toString());
|
||||
}
|
||||
|
||||
if (channelConfiguration.powerCommandTopic != null) {
|
||||
buildChannel(STATE_CHANNEL_ID, ComponentChannelType.SWITCH,
|
||||
new OnOffValue(onStates.toArray(new String[0]), new String[] { MODE_OFF },
|
||||
channelConfiguration.payloadOn, channelConfiguration.payloadOff),
|
||||
"State", componentConfiguration.getUpdateListener())
|
||||
.stateTopic(channelConfiguration.modeStateTopic, channelConfiguration.modeStateTemplate,
|
||||
channelConfiguration.getValueTemplate())
|
||||
.commandTopic(channelConfiguration.powerCommandTopic, channelConfiguration.isRetain(),
|
||||
channelConfiguration.getQos(), channelConfiguration.powerCommandTemplate)
|
||||
.inferOptimistic(channelConfiguration.optimistic).build();
|
||||
}
|
||||
|
||||
if (channelConfiguration.modeCommandTopic != null | channelConfiguration.modeStateTopic != null) {
|
||||
buildChannel(MODE_CHANNEL_ID, ComponentChannelType.STRING,
|
||||
new TextValue(channelConfiguration.modes.toArray(new String[0])), "Mode",
|
||||
componentConfiguration.getUpdateListener())
|
||||
.stateTopic(channelConfiguration.modeStateTopic, channelConfiguration.modeStateTemplate,
|
||||
channelConfiguration.getValueTemplate())
|
||||
.commandTopic(channelConfiguration.modeCommandTopic, channelConfiguration.isRetain(),
|
||||
channelConfiguration.getQos(), channelConfiguration.modeCommandTemplate)
|
||||
.inferOptimistic(channelConfiguration.optimistic).build();
|
||||
}
|
||||
|
||||
if (channelConfiguration.currentTemperatureTopic != null) {
|
||||
buildChannel(CURRENT_TEMPERATURE_CHANNEL_ID, ComponentChannelType.TEMPERATURE,
|
||||
new NumberValue(null, null, null, temperatureUnit.getUnit()), "Current Temperature",
|
||||
componentConfiguration.getUpdateListener())
|
||||
.stateTopic(channelConfiguration.currentTemperatureTopic,
|
||||
channelConfiguration.currentTemperatureTemplate, channelConfiguration.getValueTemplate())
|
||||
.build();
|
||||
}
|
||||
|
||||
if (channelConfiguration.temperatureStateTopic != null
|
||||
|| channelConfiguration.temperatureCommandTopic != null) {
|
||||
buildChannel(TARGET_TEMPERATURE_CHANNEL_ID, ComponentChannelType.TEMPERATURE,
|
||||
new NumberValue(channelConfiguration.minTemp, channelConfiguration.maxTemp, precision,
|
||||
temperatureUnit.getUnit()),
|
||||
"Target Temperature", componentConfiguration.getUpdateListener())
|
||||
.stateTopic(channelConfiguration.temperatureStateTopic,
|
||||
channelConfiguration.temperatureStateTemplate, channelConfiguration.getValueTemplate())
|
||||
.commandTopic(channelConfiguration.temperatureCommandTopic, channelConfiguration.isRetain(),
|
||||
channelConfiguration.getQos(), channelConfiguration.temperatureCommandTemplate)
|
||||
.inferOptimistic(channelConfiguration.optimistic).build();
|
||||
}
|
||||
|
||||
finalizeChannels();
|
||||
}
|
||||
}
|
@ -47,6 +47,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.config.ChannelConfigurati
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.exception.ConfigurationException;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.config.core.validation.ConfigValidationException;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
@ -95,6 +96,7 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
|
||||
protected final MqttChannelStateDescriptionProvider stateDescriptionProvider;
|
||||
protected final ChannelTypeRegistry channelTypeRegistry;
|
||||
protected final Jinjava jinjava;
|
||||
protected final UnitProvider unitProvider;
|
||||
public final int attributeReceiveTimeout;
|
||||
protected final DelayedBatchProcessing<Object> delayedProcessing;
|
||||
protected final DiscoverComponents discoverComponents;
|
||||
@ -123,20 +125,21 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
|
||||
*/
|
||||
public HomeAssistantThingHandler(Thing thing, MqttChannelTypeProvider channelTypeProvider,
|
||||
MqttChannelStateDescriptionProvider stateDescriptionProvider, ChannelTypeRegistry channelTypeRegistry,
|
||||
Jinjava jinjava, int subscribeTimeout, int attributeReceiveTimeout) {
|
||||
Jinjava jinjava, UnitProvider unitProvider, int subscribeTimeout, int attributeReceiveTimeout) {
|
||||
super(thing, subscribeTimeout);
|
||||
this.gson = new GsonBuilder().registerTypeAdapterFactory(new ChannelConfigurationTypeAdapterFactory()).create();
|
||||
this.channelTypeProvider = channelTypeProvider;
|
||||
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||
this.channelTypeRegistry = channelTypeRegistry;
|
||||
this.jinjava = jinjava;
|
||||
this.unitProvider = unitProvider;
|
||||
this.attributeReceiveTimeout = attributeReceiveTimeout;
|
||||
this.delayedProcessing = new DelayedBatchProcessing<>(attributeReceiveTimeout, this, scheduler);
|
||||
|
||||
newStyleChannels = "true".equals(thing.getProperties().get("newStyleChannels"));
|
||||
|
||||
this.discoverComponents = new DiscoverComponents(thing.getUID(), scheduler, this, this, gson, jinjava,
|
||||
newStyleChannels);
|
||||
unitProvider, newStyleChannels);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -184,7 +187,8 @@ public class HomeAssistantThingHandler extends AbstractMQTTThingHandler
|
||||
String channelConfigurationJSON = (String) channelConfig.get("config");
|
||||
try {
|
||||
AbstractComponent<?> component = ComponentFactory.createComponent(thingUID, haID,
|
||||
channelConfigurationJSON, this, this, scheduler, gson, jinjava, newStyleChannels);
|
||||
channelConfigurationJSON, this, this, scheduler, gson, jinjava, unitProvider,
|
||||
newStyleChannels);
|
||||
if (typeID.equals(MqttBindingConstants.HOMEASSISTANT_MQTT_THING)) {
|
||||
typeID = calculateThingTypeUID(component);
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ channel-type.mqtt.ha-string-advanced.label = String
|
||||
channel-type.mqtt.ha-string.label = String
|
||||
channel-type.mqtt.ha-switch-advanced.label = Switch
|
||||
channel-type.mqtt.ha-switch.label = Switch
|
||||
channel-type.mqtt.ha-temperature.label = Temperature
|
||||
channel-type.mqtt.ha-trigger-advanced.label = Trigger
|
||||
channel-type.mqtt.ha-trigger.label = Trigger
|
||||
|
||||
|
@ -66,6 +66,12 @@
|
||||
<config-description-ref uri="channel-type:mqtt:ha-channel"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="ha-temperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temperature</label>
|
||||
<config-description-ref uri="channel-type:mqtt:ha-channel"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="ha-trigger">
|
||||
<kind>trigger</kind>
|
||||
<label>Trigger</label>
|
||||
|
@ -32,6 +32,7 @@ import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider;
|
||||
import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider;
|
||||
import org.openhab.binding.mqtt.homeassistant.generic.internal.MqttThingHandlerFactory;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.component.AbstractComponent;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
import org.openhab.core.test.storage.VolatileStorageService;
|
||||
import org.openhab.core.thing.type.ChannelTypeRegistry;
|
||||
import org.openhab.core.thing.type.ThingTypeRegistry;
|
||||
@ -44,6 +45,7 @@ import org.openhab.core.thing.type.ThingTypeRegistry;
|
||||
@NonNullByDefault
|
||||
public class HomeAssistantChannelTransformationTests {
|
||||
protected @Mock @NonNullByDefault({}) ThingTypeRegistry thingTypeRegistry;
|
||||
protected @Mock @NonNullByDefault({}) UnitProvider unitProvider;
|
||||
|
||||
protected @NonNullByDefault({}) HomeAssistantChannelTransformation transformation;
|
||||
|
||||
@ -54,7 +56,7 @@ public class HomeAssistantChannelTransformationTests {
|
||||
MqttChannelStateDescriptionProvider stateDescriptionProvider = new MqttChannelStateDescriptionProvider();
|
||||
ChannelTypeRegistry channelTypeRegistry = new ChannelTypeRegistry();
|
||||
MqttThingHandlerFactory thingHandlerFactory = new MqttThingHandlerFactory(channelTypeProvider,
|
||||
stateDescriptionProvider, channelTypeRegistry);
|
||||
stateDescriptionProvider, channelTypeRegistry, unitProvider);
|
||||
|
||||
AbstractComponent component = Mockito.mock(AbstractComponent.class);
|
||||
HaID haID = new HaID("homeassistant/light/pool/light/config");
|
||||
|
@ -41,6 +41,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.HaID;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.HandlerConfiguration;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.handler.HomeAssistantThingHandler;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
@ -64,6 +65,7 @@ public abstract class AbstractComponentTests extends AbstractHomeAssistantTests
|
||||
|
||||
private @Mock @NonNullByDefault({}) ThingHandlerCallback callbackMock;
|
||||
private @NonNullByDefault({}) LatchThingHandler thingHandler;
|
||||
protected @Mock @NonNullByDefault({}) UnitProvider unitProvider;
|
||||
|
||||
@BeforeEach
|
||||
public void setupThingHandler() {
|
||||
@ -84,7 +86,7 @@ public abstract class AbstractComponentTests extends AbstractHomeAssistantTests
|
||||
haThing.setProperty("newStyleChannels", "true");
|
||||
}
|
||||
thingHandler = new LatchThingHandler(haThing, channelTypeProvider, stateDescriptionProvider,
|
||||
channelTypeRegistry, SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT);
|
||||
channelTypeRegistry, unitProvider, SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT);
|
||||
thingHandler.setConnection(bridgeConnection);
|
||||
thingHandler.setCallback(callbackMock);
|
||||
thingHandler = spy(thingHandler);
|
||||
@ -341,9 +343,9 @@ public abstract class AbstractComponentTests extends AbstractHomeAssistantTests
|
||||
|
||||
public LatchThingHandler(Thing thing, MqttChannelTypeProvider channelTypeProvider,
|
||||
MqttChannelStateDescriptionProvider stateDescriptionProvider, ChannelTypeRegistry channelTypeRegistry,
|
||||
int subscribeTimeout, int attributeReceiveTimeout) {
|
||||
UnitProvider unitProvider, int subscribeTimeout, int attributeReceiveTimeout) {
|
||||
super(thing, channelTypeProvider, stateDescriptionProvider, channelTypeRegistry, new Jinjava(),
|
||||
subscribeTimeout, attributeReceiveTimeout);
|
||||
unitProvider, subscribeTimeout, attributeReceiveTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,159 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.mqtt.homeassistant.internal.component;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.measure.quantity.Temperature;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.mqtt.generic.values.NumberValue;
|
||||
import org.openhab.binding.mqtt.generic.values.OnOffValue;
|
||||
import org.openhab.binding.mqtt.generic.values.TextValue;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
|
||||
/**
|
||||
* Tests for {@link WaterHeater}
|
||||
*
|
||||
* @author Cody Cutrer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WaterHeaterTests extends AbstractComponentTests {
|
||||
public static final String CONFIG_TOPIC = "water_heater/boiler";
|
||||
|
||||
@SuppressWarnings("null")
|
||||
@Test
|
||||
public void test() {
|
||||
when(unitProvider.getUnit(Temperature.class)).thenReturn(ImperialUnits.FAHRENHEIT);
|
||||
|
||||
var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC), """
|
||||
{
|
||||
"platform": "water_heater",
|
||||
"name": "Boiler",
|
||||
"modes": [
|
||||
"off",
|
||||
"eco",
|
||||
"performance"
|
||||
],
|
||||
"mode_state_topic": "basement/boiler/mode",
|
||||
"mode_command_topic": "basement/boiler/mode/set",
|
||||
"mode_command_template": "{{ value if value==\\"off\\" else \\"on\\" }}",
|
||||
"temperature_state_topic": "basement/boiler/temperature",
|
||||
"temperature_command_topic": "basement/boiler/temperature/set",
|
||||
"current_temperature_topic": "basement/boiler/current_temperature",
|
||||
"precision": 1.0
|
||||
}
|
||||
""");
|
||||
|
||||
assertThat(component.channels.size(), is(3));
|
||||
assertThat(component.getName(), is("Boiler"));
|
||||
|
||||
assertChannel(component, WaterHeater.MODE_CHANNEL_ID, "basement/boiler/mode", "basement/boiler/mode/set",
|
||||
"Mode", TextValue.class);
|
||||
assertChannel(component, WaterHeater.CURRENT_TEMPERATURE_CHANNEL_ID, "basement/boiler/current_temperature", "",
|
||||
"Current Temperature", NumberValue.class);
|
||||
assertChannel(component, WaterHeater.TARGET_TEMPERATURE_CHANNEL_ID, "basement/boiler/temperature",
|
||||
"basement/boiler/temperature/set", "Target Temperature", NumberValue.class);
|
||||
|
||||
publishMessage("basement/boiler/mode", "eco");
|
||||
assertState(component, WaterHeater.MODE_CHANNEL_ID, new StringType("eco"));
|
||||
publishMessage("basement/boiler/mode", "invalid");
|
||||
assertState(component, WaterHeater.MODE_CHANNEL_ID, new StringType("eco"));
|
||||
|
||||
publishMessage("basement/boiler/current_temperature", "120");
|
||||
assertState(component, WaterHeater.CURRENT_TEMPERATURE_CHANNEL_ID,
|
||||
new QuantityType<>(120, ImperialUnits.FAHRENHEIT));
|
||||
publishMessage("basement/boiler/temperature", "125");
|
||||
assertState(component, WaterHeater.TARGET_TEMPERATURE_CHANNEL_ID,
|
||||
new QuantityType<>(125, ImperialUnits.FAHRENHEIT));
|
||||
|
||||
component.getChannel(WaterHeater.MODE_CHANNEL_ID).getState().publishValue(new StringType("eco"));
|
||||
assertPublished("basement/boiler/mode/set", "on");
|
||||
component.getChannel(WaterHeater.MODE_CHANNEL_ID).getState().publishValue(new StringType("off"));
|
||||
assertPublished("basement/boiler/mode/set", "off");
|
||||
|
||||
component.getChannel(WaterHeater.TARGET_TEMPERATURE_CHANNEL_ID).getState().publishValue(new DecimalType(130));
|
||||
assertPublished("basement/boiler/temperature/set", "130");
|
||||
}
|
||||
|
||||
@SuppressWarnings("null")
|
||||
@Test
|
||||
public void testSynthesizedPowerState() {
|
||||
when(unitProvider.getUnit(Temperature.class)).thenReturn(ImperialUnits.FAHRENHEIT);
|
||||
|
||||
var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC), """
|
||||
{
|
||||
"platform": "water_heater",
|
||||
"name": "Boiler",
|
||||
"modes": [
|
||||
"off",
|
||||
"eco",
|
||||
"performance"
|
||||
],
|
||||
"mode_state_topic": "basement/boiler/mode",
|
||||
"mode_command_topic": "basement/boiler/mode/set",
|
||||
"temperature_state_topic": "basement/boiler/temperature",
|
||||
"temperature_command_topic": "basement/boiler/temperature/set",
|
||||
"current_temperature_topic": "basement/boiler/current_temperature",
|
||||
"precision": 1.0,
|
||||
"power_command_topic": "basement/boiler/power/set"
|
||||
}
|
||||
""");
|
||||
|
||||
assertThat(component.channels.size(), is(4));
|
||||
assertThat(component.getName(), is("Boiler"));
|
||||
|
||||
assertChannel(component, WaterHeater.STATE_CHANNEL_ID, "basement/boiler/mode", "basement/boiler/power/set",
|
||||
"State", OnOffValue.class);
|
||||
assertChannel(component, WaterHeater.MODE_CHANNEL_ID, "basement/boiler/mode", "basement/boiler/mode/set",
|
||||
"Mode", TextValue.class);
|
||||
assertChannel(component, WaterHeater.CURRENT_TEMPERATURE_CHANNEL_ID, "basement/boiler/current_temperature", "",
|
||||
"Current Temperature", NumberValue.class);
|
||||
assertChannel(component, WaterHeater.TARGET_TEMPERATURE_CHANNEL_ID, "basement/boiler/temperature",
|
||||
"basement/boiler/temperature/set", "Target Temperature", NumberValue.class);
|
||||
|
||||
publishMessage("basement/boiler/mode", "eco");
|
||||
assertState(component, WaterHeater.MODE_CHANNEL_ID, new StringType("eco"));
|
||||
assertState(component, WaterHeater.STATE_CHANNEL_ID, OnOffType.ON);
|
||||
publishMessage("basement/boiler/mode", "invalid");
|
||||
assertState(component, WaterHeater.MODE_CHANNEL_ID, new StringType("eco"));
|
||||
assertState(component, WaterHeater.STATE_CHANNEL_ID, OnOffType.ON);
|
||||
publishMessage("basement/boiler/mode", "off");
|
||||
assertState(component, WaterHeater.MODE_CHANNEL_ID, new StringType("off"));
|
||||
assertState(component, WaterHeater.STATE_CHANNEL_ID, OnOffType.OFF);
|
||||
|
||||
component.getChannel(WaterHeater.MODE_CHANNEL_ID).getState().publishValue(new StringType("eco"));
|
||||
assertPublished("basement/boiler/mode/set", "eco");
|
||||
component.getChannel(WaterHeater.MODE_CHANNEL_ID).getState().publishValue(new StringType("off"));
|
||||
assertPublished("basement/boiler/mode/set", "off");
|
||||
|
||||
component.getChannel(WaterHeater.STATE_CHANNEL_ID).getState().publishValue(OnOffType.ON);
|
||||
assertPublished("basement/boiler/power/set", "ON");
|
||||
component.getChannel(WaterHeater.STATE_CHANNEL_ID).getState().publishValue(OnOffType.OFF);
|
||||
assertPublished("basement/boiler/power/set", "OFF");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<String> getConfigTopics() {
|
||||
return Set.of(CONFIG_TOPIC);
|
||||
}
|
||||
}
|
@ -38,6 +38,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.component.Climate;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.component.Sensor;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.component.Switch;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
import org.openhab.core.library.CoreItemFactory;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
@ -72,6 +73,7 @@ public class HomeAssistantThingHandlerTests extends AbstractHomeAssistantTests {
|
||||
private @Mock @NonNullByDefault({}) ThingHandlerCallback callbackMock;
|
||||
private @NonNullByDefault({}) HomeAssistantThingHandler thingHandler;
|
||||
private @NonNullByDefault({}) HomeAssistantThingHandler nonSpyThingHandler;
|
||||
private @Mock @NonNullByDefault({}) UnitProvider unitProvider;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
@ -87,7 +89,7 @@ public class HomeAssistantThingHandlerTests extends AbstractHomeAssistantTests {
|
||||
|
||||
protected void setupThingHandler() {
|
||||
thingHandler = new HomeAssistantThingHandler(haThing, channelTypeProvider, stateDescriptionProvider,
|
||||
channelTypeRegistry, new Jinjava(), SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT);
|
||||
channelTypeRegistry, new Jinjava(), unitProvider, SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT);
|
||||
thingHandler.setConnection(bridgeConnection);
|
||||
thingHandler.setCallback(callbackMock);
|
||||
nonSpyThingHandler = thingHandler;
|
||||
@ -409,7 +411,7 @@ public class HomeAssistantThingHandlerTests extends AbstractHomeAssistantTests {
|
||||
public void testDuplicateChannelIdNewStyleChannels() {
|
||||
haThing.setProperty("newStyleChannels", "true");
|
||||
thingHandler = new HomeAssistantThingHandler(haThing, channelTypeProvider, stateDescriptionProvider,
|
||||
channelTypeRegistry, new Jinjava(), SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT);
|
||||
channelTypeRegistry, new Jinjava(), unitProvider, SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT);
|
||||
thingHandler.setConnection(bridgeConnection);
|
||||
thingHandler.setCallback(callbackMock);
|
||||
nonSpyThingHandler = thingHandler;
|
||||
@ -466,7 +468,7 @@ public class HomeAssistantThingHandlerTests extends AbstractHomeAssistantTests {
|
||||
public void testDuplicateChannelIdNewStyleChannelsComplex() {
|
||||
haThing.setProperty("newStyleChannels", "true");
|
||||
thingHandler = new HomeAssistantThingHandler(haThing, channelTypeProvider, stateDescriptionProvider,
|
||||
channelTypeRegistry, new Jinjava(), SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT);
|
||||
channelTypeRegistry, new Jinjava(), unitProvider, SUBSCRIBE_TIMEOUT, ATTRIBUTE_RECEIVE_TIMEOUT);
|
||||
thingHandler.setConnection(bridgeConnection);
|
||||
thingHandler.setCallback(callbackMock);
|
||||
nonSpyThingHandler = thingHandler;
|
||||
|
@ -41,6 +41,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents.Compon
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.HaID;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.HandlerConfiguration;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.config.ChannelConfigurationTypeAdapterFactory;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
|
||||
import org.openhab.core.test.java.JavaOSGiTest;
|
||||
|
||||
@ -81,9 +82,10 @@ public class DiscoverComponentsTest extends JavaOSGiTest {
|
||||
|
||||
Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ChannelConfigurationTypeAdapterFactory()).create();
|
||||
Jinjava jinjava = new Jinjava();
|
||||
UnitProvider unitProvider = mock(UnitProvider.class);
|
||||
|
||||
DiscoverComponents discover = spy(new DiscoverComponents(ThingChannelConstants.TEST_HOME_ASSISTANT_THING,
|
||||
scheduler, channelStateUpdateListener, availabilityTracker, gson, jinjava, true));
|
||||
scheduler, channelStateUpdateListener, availabilityTracker, gson, jinjava, unitProvider, true));
|
||||
|
||||
HandlerConfiguration config = new HandlerConfiguration("homeassistant", List.of("switch/object"));
|
||||
|
||||
|
@ -48,6 +48,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.HaID;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.component.AbstractComponent;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.component.Switch;
|
||||
import org.openhab.binding.mqtt.homeassistant.internal.config.ChannelConfigurationTypeAdapterFactory;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
|
||||
import org.openhab.core.io.transport.mqtt.MqttConnectionObserver;
|
||||
import org.openhab.core.io.transport.mqtt.MqttConnectionState;
|
||||
@ -161,6 +162,7 @@ public class HomeAssistantMQTTImplementationTest extends MqttOSGiTest {
|
||||
@Test
|
||||
public void parseHATree() throws Exception {
|
||||
MqttChannelTypeProvider channelTypeProvider = mock(MqttChannelTypeProvider.class);
|
||||
UnitProvider unitProvider = mock(UnitProvider.class);
|
||||
|
||||
final Map<String, AbstractComponent<?>> haComponents = new HashMap<>();
|
||||
Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ChannelConfigurationTypeAdapterFactory()).create();
|
||||
@ -168,7 +170,7 @@ public class HomeAssistantMQTTImplementationTest extends MqttOSGiTest {
|
||||
|
||||
ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(4);
|
||||
DiscoverComponents discover = spy(new DiscoverComponents(ThingChannelConstants.TEST_HOME_ASSISTANT_THING,
|
||||
scheduler, channelStateUpdateListener, availabilityTracker, gson, jinjava, true));
|
||||
scheduler, channelStateUpdateListener, availabilityTracker, gson, jinjava, unitProvider, true));
|
||||
|
||||
// The DiscoverComponents object calls ComponentDiscovered callbacks.
|
||||
// In the following implementation we add the found component to the `haComponents` map
|
||||
|
Loading…
Reference in New Issue
Block a user