[mqtt-homeassistant] Refactoring: fixed under_score/CamelCase usages and nullable annotations (#11120)

Signed-off-by: Anton Kharuzhy <antroids@gmail.com>
This commit is contained in:
antroids
2021-09-19 23:04:14 +03:00
committed by GitHub
parent 2fb86d7138
commit e1e9f90da1
34 changed files with 672 additions and 556 deletions

View File

@@ -125,8 +125,8 @@ public class ComponentChannel {
private final String label;
private final ChannelStateUpdateListener channelStateUpdateListener;
private @Nullable String state_topic;
private @Nullable String command_topic;
private @Nullable String stateTopic;
private @Nullable String commandTopic;
private boolean retain;
private boolean trigger;
private @Nullable Integer qos;
@@ -144,14 +144,14 @@ public class ComponentChannel {
this.channelStateUpdateListener = channelStateUpdateListener;
}
public Builder stateTopic(@Nullable String state_topic) {
this.state_topic = state_topic;
public Builder stateTopic(@Nullable String stateTopic) {
this.stateTopic = stateTopic;
return this;
}
public Builder stateTopic(@Nullable String state_topic, @Nullable String... templates) {
this.state_topic = state_topic;
if (state_topic != null && !state_topic.isBlank()) {
public Builder stateTopic(@Nullable String stateTopic, @Nullable String... templates) {
this.stateTopic = stateTopic;
if (stateTopic != null && !stateTopic.isBlank()) {
for (String template : templates) {
if (template != null && !template.isBlank()) {
this.templateIn = template;
@@ -164,27 +164,26 @@ public class ComponentChannel {
/**
* @deprecated use commandTopic(String, boolean, int)
* @param command_topic topic
* @param commandTopic topic
* @param retain retain
* @return this
*/
@Deprecated
public Builder commandTopic(@Nullable String command_topic, boolean retain) {
this.command_topic = command_topic;
public Builder commandTopic(@Nullable String commandTopic, boolean retain) {
this.commandTopic = commandTopic;
this.retain = retain;
return this;
}
public Builder commandTopic(@Nullable String command_topic, boolean retain, int qos) {
return commandTopic(command_topic, retain, qos, null);
public Builder commandTopic(@Nullable String commandTopic, boolean retain, int qos) {
return commandTopic(commandTopic, retain, qos, null);
}
public Builder commandTopic(@Nullable String command_topic, boolean retain, int qos,
@Nullable String template) {
this.command_topic = command_topic;
public Builder commandTopic(@Nullable String commandTopic, boolean retain, int qos, @Nullable String template) {
this.commandTopic = commandTopic;
this.retain = retain;
this.qos = qos;
if (command_topic != null && !command_topic.isBlank()) {
if (commandTopic != null && !commandTopic.isBlank()) {
this.templateOut = template;
}
return this;
@@ -215,16 +214,16 @@ public class ComponentChannel {
channelTypeUID = new ChannelTypeUID(MqttBindingConstants.BINDING_ID,
channelUID.getGroupId() + "_" + channelID);
channelState = new HomeAssistantChannelState(
ChannelConfigBuilder.create().withRetain(retain).withQos(qos).withStateTopic(state_topic)
.withCommandTopic(command_topic).makeTrigger(trigger).build(),
ChannelConfigBuilder.create().withRetain(retain).withQos(qos).withStateTopic(stateTopic)
.withCommandTopic(commandTopic).makeTrigger(trigger).build(),
channelUID, valueState, channelStateUpdateListener, commandFilter);
String localStateTopic = state_topic;
String localStateTopic = stateTopic;
if (localStateTopic == null || localStateTopic.isBlank() || this.trigger) {
type = ChannelTypeBuilder.trigger(channelTypeUID, label)
.withConfigDescriptionURI(URI.create(MqttBindingConstants.CONFIG_HA_CHANNEL)).build();
} else {
StateDescriptionFragment description = valueState.createStateDescription(command_topic == null).build();
StateDescriptionFragment description = valueState.createStateDescription(commandTopic == null).build();
type = ChannelTypeBuilder.state(channelTypeUID, label, channelState.getItemType())
.withConfigDescriptionURI(URI.create(MqttBindingConstants.CONFIG_HA_CHANNEL))
.withStateDescriptionFragment(description).build();

View File

@@ -250,10 +250,7 @@ public class HaID {
if (!nodeID.equals(other.nodeID)) {
return false;
}
if (!objectID.equals(other.objectID)) {
return false;
}
return true;
return objectID.equals(other.objectID);
}
@Override

View File

@@ -86,9 +86,9 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
this.configSeen = false;
String availability_topic = this.channelConfiguration.getAvailabilityTopic();
if (availability_topic != null) {
componentConfiguration.getTracker().addAvailabilityTopic(availability_topic,
String availabilityTopic = this.channelConfiguration.getAvailabilityTopic();
if (availabilityTopic != null) {
componentConfiguration.getTracker().addAvailabilityTopic(availabilityTopic,
this.channelConfiguration.getPayloadAvailable(),
this.channelConfiguration.getPayloadNotAvailable());
}
@@ -180,7 +180,7 @@ public abstract class AbstractComponent<C extends AbstractChannelConfiguration>
/**
* Return a components channel. A HomeAssistant MQTT component consists of multiple functions
* and those are mapped to one or more channels. The channel IDs are constants within the
* derived Component, like the {@link Switch#switchChannelID}.
* derived Component, like the {@link Switch#SWITCH_CHANNEL_ID}.
*
* @param channelID The channel ID
* @return A components channel

View File

@@ -17,6 +17,8 @@ import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mqtt.generic.values.TextValue;
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
import com.google.gson.annotations.SerializedName;
/**
* A MQTT alarm control panel, following the https://www.home-assistant.io/components/alarm_control_panel.mqtt/
* specification.
@@ -28,10 +30,10 @@ import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChanne
*/
@NonNullByDefault
public class AlarmControlPanel extends AbstractComponent<AlarmControlPanel.ChannelConfiguration> {
public static final String stateChannelID = "alarm"; // Randomly chosen channel "ID"
public static final String switchDisarmChannelID = "disarm"; // Randomly chosen channel "ID"
public static final String switchArmHomeChannelID = "armhome"; // Randomly chosen channel "ID"
public static final String switchArmAwayChannelID = "armaway"; // Randomly chosen channel "ID"
public static final String STATE_CHANNEL_ID = "alarm"; // Randomly chosen channel "ID"
public static final String SWITCH_DISARM_CHANNEL_ID = "disarm"; // Randomly chosen channel "ID"
public static final String SWITCH_ARM_HOME_CHANNEL_ID = "armhome"; // Randomly chosen channel "ID"
public static final String SWITCH_ARM_AWAY_CHANNEL_ID = "armaway"; // Randomly chosen channel "ID"
/**
* Configuration class for MQTT component
@@ -43,45 +45,57 @@ public class AlarmControlPanel extends AbstractComponent<AlarmControlPanel.Chann
protected @Nullable String code;
protected String state_topic = "";
protected String state_disarmed = "disarmed";
protected String state_armed_home = "armed_home";
protected String state_armed_away = "armed_away";
protected String state_pending = "pending";
protected String state_triggered = "triggered";
@SerializedName("state_topic")
protected String stateTopic = "";
@SerializedName("state_disarmed")
protected String stateDisarmed = "disarmed";
@SerializedName("state_armed_home")
protected String stateArmedHome = "armed_home";
@SerializedName("state_armed_away")
protected String stateArmedAway = "armed_away";
@SerializedName("state_pending")
protected String statePending = "pending";
@SerializedName("state_triggered")
protected String stateTriggered = "triggered";
protected @Nullable String command_topic;
protected String payload_disarm = "DISARM";
protected String payload_arm_home = "ARM_HOME";
protected String payload_arm_away = "ARM_AWAY";
@SerializedName("command_topic")
protected @Nullable String commandTopic;
@SerializedName("payload_disarm")
protected String payloadDisarm = "DISARM";
@SerializedName("payload_arm_home")
protected String payloadArmHome = "ARM_HOME";
@SerializedName("payload_arm_away")
protected String payloadArmAway = "ARM_AWAY";
}
public AlarmControlPanel(ComponentFactory.ComponentConfiguration componentConfiguration) {
super(componentConfiguration, ChannelConfiguration.class);
final String[] state_enum = { channelConfiguration.state_disarmed, channelConfiguration.state_armed_home,
channelConfiguration.state_armed_away, channelConfiguration.state_pending,
channelConfiguration.state_triggered };
buildChannel(stateChannelID, new TextValue(state_enum), channelConfiguration.getName(),
final String[] stateEnum = { channelConfiguration.stateDisarmed, channelConfiguration.stateArmedHome,
channelConfiguration.stateArmedAway, channelConfiguration.statePending,
channelConfiguration.stateTriggered };
buildChannel(STATE_CHANNEL_ID, new TextValue(stateEnum), channelConfiguration.getName(),
componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.state_topic, channelConfiguration.getValueTemplate())//
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())//
.build();
String command_topic = channelConfiguration.command_topic;
if (command_topic != null) {
buildChannel(switchDisarmChannelID, new TextValue(new String[] { channelConfiguration.payload_disarm }),
String commandTopic = channelConfiguration.commandTopic;
if (commandTopic != null) {
buildChannel(SWITCH_DISARM_CHANNEL_ID, new TextValue(new String[] { channelConfiguration.payloadDisarm }),
channelConfiguration.getName(), componentConfiguration.getUpdateListener())
.commandTopic(command_topic, channelConfiguration.isRetain(), channelConfiguration.getQos())
.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos())
.build();
buildChannel(switchArmHomeChannelID, new TextValue(new String[] { channelConfiguration.payload_arm_home }),
channelConfiguration.getName(), componentConfiguration.getUpdateListener())
.commandTopic(command_topic, channelConfiguration.isRetain(), channelConfiguration.getQos())
buildChannel(SWITCH_ARM_HOME_CHANNEL_ID,
new TextValue(new String[] { channelConfiguration.payloadArmHome }), channelConfiguration.getName(),
componentConfiguration.getUpdateListener())
.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos())
.build();
buildChannel(switchArmAwayChannelID, new TextValue(new String[] { channelConfiguration.payload_arm_away }),
channelConfiguration.getName(), componentConfiguration.getUpdateListener())
.commandTopic(command_topic, channelConfiguration.isRetain(), channelConfiguration.getQos())
buildChannel(SWITCH_ARM_AWAY_CHANNEL_ID,
new TextValue(new String[] { channelConfiguration.payloadArmAway }), channelConfiguration.getName(),
componentConfiguration.getUpdateListener())
.commandTopic(commandTopic, channelConfiguration.isRetain(), channelConfiguration.getQos())
.build();
}
}

View File

@@ -23,6 +23,8 @@ import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChanne
import org.openhab.binding.mqtt.homeassistant.internal.listener.ExpireUpdateStateListener;
import org.openhab.binding.mqtt.homeassistant.internal.listener.OffDelayUpdateStateListener;
import com.google.gson.annotations.SerializedName;
/**
* A MQTT BinarySensor, following the https://www.home-assistant.io/components/binary_sensor.mqtt/ specification.
*
@@ -30,7 +32,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.listener.OffDelayUpdateSt
*/
@NonNullByDefault
public class BinarySensor extends AbstractComponent<BinarySensor.ChannelConfiguration> {
public static final String sensorChannelID = "sensor"; // Randomly chosen channel "ID"
public static final String SENSOR_CHANNEL_ID = "sensor"; // Randomly chosen channel "ID"
/**
* Configuration class for MQTT component
@@ -40,39 +42,49 @@ public class BinarySensor extends AbstractComponent<BinarySensor.ChannelConfigur
super("MQTT Binary Sensor");
}
protected @Nullable String device_class;
protected boolean force_update = false;
protected @Nullable Integer expire_after;
protected @Nullable Integer off_delay;
@SerializedName("device_class")
protected @Nullable String deviceClass;
@SerializedName("force_update")
protected boolean forceUpdate = false;
@SerializedName("expire_after")
protected @Nullable Integer expireAfter;
@SerializedName("off_delay")
protected @Nullable Integer offDelay;
protected String state_topic = "";
protected String payload_on = "ON";
protected String payload_off = "OFF";
@SerializedName("state_topic")
protected String stateTopic = "";
@SerializedName("payload_on")
protected String payloadOn = "ON";
@SerializedName("payload_off")
protected String payloadOff = "OFF";
protected @Nullable String json_attributes_topic;
protected @Nullable String json_attributes_template;
protected @Nullable List<String> json_attributes;
@SerializedName("json_attributes_topic")
protected @Nullable String jsonAttributesTopic;
@SerializedName("json_attributes_template")
protected @Nullable String jsonAttributesTemplate;
@SerializedName("json_attributes")
protected @Nullable List<String> jsonAttributes;
}
public BinarySensor(ComponentFactory.ComponentConfiguration componentConfiguration) {
super(componentConfiguration, ChannelConfiguration.class);
OnOffValue value = new OnOffValue(channelConfiguration.payload_on, channelConfiguration.payload_off);
OnOffValue value = new OnOffValue(channelConfiguration.payloadOn, channelConfiguration.payloadOff);
buildChannel(sensorChannelID, value, "value", getListener(componentConfiguration, value))
.stateTopic(channelConfiguration.state_topic, channelConfiguration.getValueTemplate()).build();
buildChannel(SENSOR_CHANNEL_ID, value, "value", getListener(componentConfiguration, value))
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate()).build();
}
private ChannelStateUpdateListener getListener(ComponentFactory.ComponentConfiguration componentConfiguration,
Value value) {
ChannelStateUpdateListener updateListener = componentConfiguration.getUpdateListener();
if (channelConfiguration.expire_after != null) {
updateListener = new ExpireUpdateStateListener(updateListener, channelConfiguration.expire_after, value,
if (channelConfiguration.expireAfter != null) {
updateListener = new ExpireUpdateStateListener(updateListener, channelConfiguration.expireAfter, value,
componentConfiguration.getTracker(), componentConfiguration.getScheduler());
}
if (channelConfiguration.off_delay != null) {
updateListener = new OffDelayUpdateStateListener(updateListener, channelConfiguration.off_delay, value,
if (channelConfiguration.offDelay != null) {
updateListener = new OffDelayUpdateStateListener(updateListener, channelConfiguration.offDelay, value,
componentConfiguration.getScheduler());
}

View File

@@ -25,7 +25,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChanne
*/
@NonNullByDefault
public class Camera extends AbstractComponent<Camera.ChannelConfiguration> {
public static final String cameraChannelID = "camera"; // Randomly chosen channel "ID"
public static final String CAMERA_CHANNEL_ID = "camera"; // Randomly chosen channel "ID"
/**
* Configuration class for MQTT component
@@ -43,7 +43,7 @@ public class Camera extends AbstractComponent<Camera.ChannelConfiguration> {
ImageValue value = new ImageValue();
buildChannel(cameraChannelID, value, channelConfiguration.getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.topic).build();
buildChannel(CAMERA_CHANNEL_ID, value, channelConfiguration.getName(),
componentConfiguration.getUpdateListener()).stateTopic(channelConfiguration.topic).build();
}
}

View File

@@ -30,6 +30,8 @@ import org.openhab.core.library.types.StringType;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import com.google.gson.annotations.SerializedName;
/**
* A MQTT climate component, following the https://www.home-assistant.io/components/climate.mqtt/ specification.
*
@@ -68,148 +70,195 @@ public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
super("MQTT HVAC");
}
protected @Nullable String action_template;
protected @Nullable String action_topic;
@SerializedName("action_template")
protected @Nullable String actionTemplate;
@SerializedName("action_topic")
protected @Nullable String actionTopic;
protected @Nullable String aux_command_topic;
protected @Nullable String aux_state_template;
protected @Nullable String aux_state_topic;
@SerializedName("aux_command_topic")
protected @Nullable String auxCommandTopic;
@SerializedName("aux_state_template")
protected @Nullable String auxStateTemplate;
@SerializedName("aux_state_topic")
protected @Nullable String auxStateTopic;
protected @Nullable String away_mode_command_topic;
protected @Nullable String away_mode_state_template;
protected @Nullable String away_mode_state_topic;
@SerializedName("away_mode_command_topic")
protected @Nullable String awayModeCommandTopic;
@SerializedName("away_mode_state_template")
protected @Nullable String awayModeStateTemplate;
@SerializedName("away_mode_state_topic")
protected @Nullable String awayModeStateTopic;
protected @Nullable String current_temperature_template;
protected @Nullable String current_temperature_topic;
@SerializedName("current_temperature_template")
protected @Nullable String currentTemperatureTemplate;
@SerializedName("current_temperature_topic")
protected @Nullable String currentTemperatureTopic;
protected @Nullable String fan_mode_command_template;
protected @Nullable String fan_mode_command_topic;
protected @Nullable String fan_mode_state_template;
protected @Nullable String fan_mode_state_topic;
protected List<String> fan_modes = Arrays.asList("auto", "low", "medium", "high");
@SerializedName("fan_mode_command_template")
protected @Nullable String fanModeCommandTemplate;
@SerializedName("fan_mode_command_topic")
protected @Nullable String fanModeCommandTopic;
@SerializedName("fan_mode_state_template")
protected @Nullable String fanModeStateTemplate;
@SerializedName("fan_mode_state_topic")
protected @Nullable String fanModeStateTopic;
@SerializedName("fan_modes")
protected List<String> fanModes = Arrays.asList("auto", "low", "medium", "high");
protected @Nullable String hold_command_template;
protected @Nullable String hold_command_topic;
protected @Nullable String hold_state_template;
protected @Nullable String hold_state_topic;
protected @Nullable List<String> hold_modes; // Are there default modes? Now the channel will be ignored without
// hold modes.
@SerializedName("hold_command_template")
protected @Nullable String holdCommandTemplate;
@SerializedName("hold_command_topic")
protected @Nullable String holdCommandTopic;
@SerializedName("hold_state_template")
protected @Nullable String holdStateTemplate;
@SerializedName("hold_state_topic")
protected @Nullable String holdStateTopic;
@SerializedName("hold_modes")
protected @Nullable List<String> holdModes; // Are there default modes? Now the channel will be ignored without
// hold modes.
protected @Nullable String json_attributes_template; // Attributes are not supported yet
protected @Nullable String json_attributes_topic;
@SerializedName("json_attributes_template")
protected @Nullable String jsonAttributesTemplate; // Attributes are not supported yet
@SerializedName("json_attributes_topic")
protected @Nullable String jsonAttributesTopic;
protected @Nullable String mode_command_template;
protected @Nullable String mode_command_topic;
protected @Nullable String mode_state_template;
protected @Nullable String mode_state_topic;
@SerializedName("mode_command_template")
protected @Nullable String modeCommandTemplate;
@SerializedName("mode_command_topic")
protected @Nullable String modeCommandTopic;
@SerializedName("mode_state_template")
protected @Nullable String modeStateTemplate;
@SerializedName("mode_state_topic")
protected @Nullable String modeStateTopic;
protected List<String> modes = Arrays.asList("auto", "off", "cool", "heat", "dry", "fan_only");
protected @Nullable String swing_command_template;
protected @Nullable String swing_command_topic;
protected @Nullable String swing_state_template;
protected @Nullable String swing_state_topic;
protected List<String> swing_modes = Arrays.asList("on", "off");
@SerializedName("swing_command_template")
protected @Nullable String swingCommandTemplate;
@SerializedName("swing_command_topic")
protected @Nullable String swingCommandTopic;
@SerializedName("swing_state_template")
protected @Nullable String swingStateTemplate;
@SerializedName("swing_state_topic")
protected @Nullable String swingStateTopic;
@SerializedName("swing_modes")
protected List<String> swingModes = Arrays.asList("on", "off");
protected @Nullable String temperature_command_template;
protected @Nullable String temperature_command_topic;
protected @Nullable String temperature_state_template;
protected @Nullable String temperature_state_topic;
@SerializedName("temperature_command_template")
protected @Nullable String temperatureCommandTemplate;
@SerializedName("temperature_command_topic")
protected @Nullable String temperatureCommandTopic;
@SerializedName("temperature_state_template")
protected @Nullable String temperatureStateTemplate;
@SerializedName("temperature_state_topic")
protected @Nullable String temperatureStateTopic;
protected @Nullable String temperature_high_command_template;
protected @Nullable String temperature_high_command_topic;
protected @Nullable String temperature_high_state_template;
protected @Nullable String temperature_high_state_topic;
@SerializedName("temperature_high_command_template")
protected @Nullable String temperatureHighCommandTemplate;
@SerializedName("temperature_high_command_topic")
protected @Nullable String temperatureHighCommandTopic;
@SerializedName("temperature_high_state_template")
protected @Nullable String temperatureHighStateTemplate;
@SerializedName("temperature_high_state_topic")
protected @Nullable String temperatureHighStateTopic;
protected @Nullable String temperature_low_command_template;
protected @Nullable String temperature_low_command_topic;
protected @Nullable String temperature_low_state_template;
protected @Nullable String temperature_low_state_topic;
@SerializedName("temperature_low_command_template")
protected @Nullable String temperatureLowCommandTemplate;
@SerializedName("temperature_low_command_topic")
protected @Nullable String temperatureLowCommandTopic;
@SerializedName("temperature_low_state_template")
protected @Nullable String temperatureLowStateTemplate;
@SerializedName("temperature_low_state_topic")
protected @Nullable String temperatureLowStateTopic;
protected @Nullable String power_command_topic;
@SerializedName("power_command_topic")
protected @Nullable String powerCommandTopic;
protected Integer initial = 21;
protected @Nullable Float max_temp;
protected @Nullable Float min_temp;
protected String temperature_unit = CELSIUM; // System unit by default
protected Float temp_step = 1f;
@SerializedName("max_temp")
protected @Nullable Float maxTemp;
@SerializedName("min_temp")
protected @Nullable Float minTemp;
@SerializedName("temperature_unit")
protected String temperatureUnit = CELSIUM; // System unit by default
@SerializedName("temp_step")
protected Float tempStep = 1f;
protected @Nullable Float precision;
protected Boolean send_if_off = true;
@SerializedName("send_if_off")
protected Boolean sendIfOff = true;
}
public Climate(ComponentFactory.ComponentConfiguration componentConfiguration) {
super(componentConfiguration, ChannelConfiguration.class);
BigDecimal minTemp = channelConfiguration.min_temp != null ? BigDecimal.valueOf(channelConfiguration.min_temp)
BigDecimal minTemp = channelConfiguration.minTemp != null ? BigDecimal.valueOf(channelConfiguration.minTemp)
: null;
BigDecimal maxTemp = channelConfiguration.max_temp != null ? BigDecimal.valueOf(channelConfiguration.max_temp)
BigDecimal maxTemp = channelConfiguration.maxTemp != null ? BigDecimal.valueOf(channelConfiguration.maxTemp)
: null;
float precision = channelConfiguration.precision != null ? channelConfiguration.precision
: (FAHRENHEIT.equals(channelConfiguration.temperature_unit) ? DEFAULT_FAHRENHEIT_PRECISION
: (FAHRENHEIT.equals(channelConfiguration.temperatureUnit) ? DEFAULT_FAHRENHEIT_PRECISION
: DEFAULT_CELSIUM_PRECISION);
final ChannelStateUpdateListener updateListener = componentConfiguration.getUpdateListener();
ComponentChannel actionChannel = buildOptionalChannel(ACTION_CH_ID,
new TextValue(ACTION_MODES.toArray(new String[0])), updateListener, null, null,
channelConfiguration.action_template, channelConfiguration.action_topic, null);
channelConfiguration.actionTemplate, channelConfiguration.actionTopic, null);
final Predicate<Command> commandFilter = channelConfiguration.send_if_off ? null
final Predicate<Command> commandFilter = channelConfiguration.sendIfOff ? null
: getCommandFilter(actionChannel);
buildOptionalChannel(AUX_CH_ID, new OnOffValue(), updateListener, null, channelConfiguration.aux_command_topic,
channelConfiguration.aux_state_template, channelConfiguration.aux_state_topic, commandFilter);
buildOptionalChannel(AUX_CH_ID, new OnOffValue(), updateListener, null, channelConfiguration.auxCommandTopic,
channelConfiguration.auxStateTemplate, channelConfiguration.auxStateTopic, commandFilter);
buildOptionalChannel(AWAY_MODE_CH_ID, new OnOffValue(), updateListener, null,
channelConfiguration.away_mode_command_topic, channelConfiguration.away_mode_state_template,
channelConfiguration.away_mode_state_topic, commandFilter);
channelConfiguration.awayModeCommandTopic, channelConfiguration.awayModeStateTemplate,
channelConfiguration.awayModeStateTopic, commandFilter);
buildOptionalChannel(CURRENT_TEMPERATURE_CH_ID,
new NumberValue(minTemp, maxTemp, BigDecimal.valueOf(precision), channelConfiguration.temperature_unit),
updateListener, null, null, channelConfiguration.current_temperature_template,
channelConfiguration.current_temperature_topic, commandFilter);
new NumberValue(minTemp, maxTemp, BigDecimal.valueOf(precision), channelConfiguration.temperatureUnit),
updateListener, null, null, channelConfiguration.currentTemperatureTemplate,
channelConfiguration.currentTemperatureTopic, commandFilter);
buildOptionalChannel(FAN_MODE_CH_ID, new TextValue(channelConfiguration.fan_modes.toArray(new String[0])),
updateListener, channelConfiguration.fan_mode_command_template,
channelConfiguration.fan_mode_command_topic, channelConfiguration.fan_mode_state_template,
channelConfiguration.fan_mode_state_topic, commandFilter);
buildOptionalChannel(FAN_MODE_CH_ID, new TextValue(channelConfiguration.fanModes.toArray(new String[0])),
updateListener, channelConfiguration.fanModeCommandTemplate, channelConfiguration.fanModeCommandTopic,
channelConfiguration.fanModeStateTemplate, channelConfiguration.fanModeStateTopic, commandFilter);
if (channelConfiguration.hold_modes != null && !channelConfiguration.hold_modes.isEmpty()) {
buildOptionalChannel(HOLD_CH_ID, new TextValue(channelConfiguration.hold_modes.toArray(new String[0])),
updateListener, channelConfiguration.hold_command_template, channelConfiguration.hold_command_topic,
channelConfiguration.hold_state_template, channelConfiguration.hold_state_topic, commandFilter);
if (channelConfiguration.holdModes != null && !channelConfiguration.holdModes.isEmpty()) {
buildOptionalChannel(HOLD_CH_ID, new TextValue(channelConfiguration.holdModes.toArray(new String[0])),
updateListener, channelConfiguration.holdCommandTemplate, channelConfiguration.holdCommandTopic,
channelConfiguration.holdStateTemplate, channelConfiguration.holdStateTopic, commandFilter);
}
buildOptionalChannel(MODE_CH_ID, new TextValue(channelConfiguration.modes.toArray(new String[0])),
updateListener, channelConfiguration.mode_command_template, channelConfiguration.mode_command_topic,
channelConfiguration.mode_state_template, channelConfiguration.mode_state_topic, commandFilter);
updateListener, channelConfiguration.modeCommandTemplate, channelConfiguration.modeCommandTopic,
channelConfiguration.modeStateTemplate, channelConfiguration.modeStateTopic, commandFilter);
buildOptionalChannel(SWING_CH_ID, new TextValue(channelConfiguration.swing_modes.toArray(new String[0])),
updateListener, channelConfiguration.swing_command_template, channelConfiguration.swing_command_topic,
channelConfiguration.swing_state_template, channelConfiguration.swing_state_topic, commandFilter);
buildOptionalChannel(SWING_CH_ID, new TextValue(channelConfiguration.swingModes.toArray(new String[0])),
updateListener, channelConfiguration.swingCommandTemplate, channelConfiguration.swingCommandTopic,
channelConfiguration.swingStateTemplate, channelConfiguration.swingStateTopic, commandFilter);
buildOptionalChannel(TEMPERATURE_CH_ID,
new NumberValue(minTemp, maxTemp, BigDecimal.valueOf(channelConfiguration.temp_step),
channelConfiguration.temperature_unit),
updateListener, channelConfiguration.temperature_command_template,
channelConfiguration.temperature_command_topic, channelConfiguration.temperature_state_template,
channelConfiguration.temperature_state_topic, commandFilter);
new NumberValue(minTemp, maxTemp, BigDecimal.valueOf(channelConfiguration.tempStep),
channelConfiguration.temperatureUnit),
updateListener, channelConfiguration.temperatureCommandTemplate,
channelConfiguration.temperatureCommandTopic, channelConfiguration.temperatureStateTemplate,
channelConfiguration.temperatureStateTopic, commandFilter);
buildOptionalChannel(TEMPERATURE_HIGH_CH_ID,
new NumberValue(minTemp, maxTemp, BigDecimal.valueOf(channelConfiguration.temp_step),
channelConfiguration.temperature_unit),
updateListener, channelConfiguration.temperature_high_command_template,
channelConfiguration.temperature_high_command_topic,
channelConfiguration.temperature_high_state_template, channelConfiguration.temperature_high_state_topic,
commandFilter);
new NumberValue(minTemp, maxTemp, BigDecimal.valueOf(channelConfiguration.tempStep),
channelConfiguration.temperatureUnit),
updateListener, channelConfiguration.temperatureHighCommandTemplate,
channelConfiguration.temperatureHighCommandTopic, channelConfiguration.temperatureHighStateTemplate,
channelConfiguration.temperatureHighStateTopic, commandFilter);
buildOptionalChannel(TEMPERATURE_LOW_CH_ID,
new NumberValue(minTemp, maxTemp, BigDecimal.valueOf(channelConfiguration.temp_step),
channelConfiguration.temperature_unit),
updateListener, channelConfiguration.temperature_low_command_template,
channelConfiguration.temperature_low_command_topic, channelConfiguration.temperature_low_state_template,
channelConfiguration.temperature_low_state_topic, commandFilter);
new NumberValue(minTemp, maxTemp, BigDecimal.valueOf(channelConfiguration.tempStep),
channelConfiguration.temperatureUnit),
updateListener, channelConfiguration.temperatureLowCommandTemplate,
channelConfiguration.temperatureLowCommandTopic, channelConfiguration.temperatureLowStateTemplate,
channelConfiguration.temperatureLowStateTopic, commandFilter);
buildOptionalChannel(POWER_CH_ID, new OnOffValue(), updateListener, null,
channelConfiguration.power_command_topic, null, null, null);
channelConfiguration.powerCommandTopic, null, null, null);
}
@Nullable

View File

@@ -35,7 +35,7 @@ import com.google.gson.Gson;
*/
@NonNullByDefault
public class ComponentFactory {
private static final Logger logger = LoggerFactory.getLogger(ComponentFactory.class);
private static final Logger LOGGER = LoggerFactory.getLogger(ComponentFactory.class);
/**
* Create a HA MQTT component. The configuration JSon string is required.
@@ -79,7 +79,7 @@ public class ComponentFactory {
return new Switch(componentConfiguration);
}
} catch (UnsupportedOperationException e) {
logger.warn("Not supported", e);
LOGGER.warn("Not supported", e);
}
return null;
}

View File

@@ -17,6 +17,8 @@ import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mqtt.generic.values.RollershutterValue;
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
import com.google.gson.annotations.SerializedName;
/**
* A MQTT Cover component, following the https://www.home-assistant.io/components/cover.mqtt/ specification.
*
@@ -26,7 +28,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChanne
*/
@NonNullByDefault
public class Cover extends AbstractComponent<Cover.ChannelConfiguration> {
public static final String switchChannelID = "cover"; // Randomly chosen channel "ID"
public static final String SWITCH_CHANNEL_ID = "cover"; // Randomly chosen channel "ID"
/**
* Configuration class for MQTT component
@@ -36,23 +38,29 @@ public class Cover extends AbstractComponent<Cover.ChannelConfiguration> {
super("MQTT Cover");
}
protected @Nullable String state_topic;
protected @Nullable String command_topic;
protected String payload_open = "OPEN";
protected String payload_close = "CLOSE";
protected String payload_stop = "STOP";
@SerializedName("state_topic")
protected @Nullable String stateTopic;
@SerializedName("command_topic")
protected @Nullable String commandTopic;
@SerializedName("payload_open")
protected String payloadOpen = "OPEN";
@SerializedName("payload_close")
protected String payloadClose = "CLOSE";
@SerializedName("payload_stop")
protected String payloadStop = "STOP";
}
public Cover(ComponentFactory.ComponentConfiguration componentConfiguration) {
super(componentConfiguration, ChannelConfiguration.class);
RollershutterValue value = new RollershutterValue(channelConfiguration.payload_open,
channelConfiguration.payload_close, channelConfiguration.payload_stop);
RollershutterValue value = new RollershutterValue(channelConfiguration.payloadOpen,
channelConfiguration.payloadClose, channelConfiguration.payloadStop);
buildChannel(switchChannelID, value, channelConfiguration.getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.state_topic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.command_topic, channelConfiguration.isRetain(),
channelConfiguration.getQos())
.build();
buildChannel(SWITCH_CHANNEL_ID, value, channelConfiguration.getName(),
componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos())
.build();
}
}

View File

@@ -17,6 +17,8 @@ import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mqtt.generic.values.OnOffValue;
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
import com.google.gson.annotations.SerializedName;
/**
* A MQTT Fan component, following the https://www.home-assistant.io/components/fan.mqtt/ specification.
*
@@ -26,7 +28,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChanne
*/
@NonNullByDefault
public class Fan extends AbstractComponent<Fan.ChannelConfiguration> {
public static final String switchChannelID = "fan"; // Randomly chosen channel "ID"
public static final String SWITCH_CHANNEL_ID = "fan"; // Randomly chosen channel "ID"
/**
* Configuration class for MQTT component
@@ -36,20 +38,25 @@ public class Fan extends AbstractComponent<Fan.ChannelConfiguration> {
super("MQTT Fan");
}
protected @Nullable String state_topic;
protected String command_topic = "";
protected String payload_on = "ON";
protected String payload_off = "OFF";
@SerializedName("state_topic")
protected @Nullable String stateTopic;
@SerializedName("command_topic")
protected String commandTopic = "";
@SerializedName("payload_on")
protected String payloadOn = "ON";
@SerializedName("payload_off")
protected String payloadOff = "OFF";
}
public Fan(ComponentFactory.ComponentConfiguration componentConfiguration) {
super(componentConfiguration, ChannelConfiguration.class);
OnOffValue value = new OnOffValue(channelConfiguration.payload_on, channelConfiguration.payload_off);
buildChannel(switchChannelID, value, channelConfiguration.getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.state_topic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.command_topic, channelConfiguration.isRetain(),
channelConfiguration.getQos())
.build();
OnOffValue value = new OnOffValue(channelConfiguration.payloadOn, channelConfiguration.payloadOff);
buildChannel(SWITCH_CHANNEL_ID, value, channelConfiguration.getName(),
componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos())
.build();
}
}

View File

@@ -29,6 +29,8 @@ import org.openhab.core.thing.ChannelUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import com.google.gson.annotations.SerializedName;
/**
* A MQTT light, following the https://www.home-assistant.io/components/light.mqtt/ specification.
*
@@ -39,9 +41,9 @@ import org.openhab.core.types.State;
*/
@NonNullByDefault
public class Light extends AbstractComponent<Light.ChannelConfiguration> implements ChannelStateUpdateListener {
public static final String switchChannelID = "light"; // Randomly chosen channel "ID"
public static final String brightnessChannelID = "brightness"; // Randomly chosen channel "ID"
public static final String colorChannelID = "color"; // Randomly chosen channel "ID"
public static final String SWITCH_CHANNEL_ID = "light"; // Randomly chosen channel "ID"
public static final String BRIGHTNESS_CHANNEL_ID = "brightness"; // Randomly chosen channel "ID"
public static final String COLOR_CHANNEL_ID = "color"; // Randomly chosen channel "ID"
/**
* Configuration class for MQTT component
@@ -51,47 +53,74 @@ public class Light extends AbstractComponent<Light.ChannelConfiguration> impleme
super("MQTT Light");
}
protected int brightness_scale = 255;
@SerializedName("brightness_scale")
protected int brightnessScale = 255;
protected boolean optimistic = false;
protected @Nullable List<String> effect_list;
@SerializedName("effect_list")
protected @Nullable List<String> effectList;
// Defines when on the payload_on is sent. Using last (the default) will send any style (brightness, color, etc)
// topics first and then a payload_on to the command_topic. Using first will send the payload_on and then any
// style topics. Using brightness will only send brightness commands instead of the payload_on to turn the light
// on.
protected String on_command_type = "last";
@SerializedName("on_command_type")
protected String onCommandType = "last";
protected @Nullable String state_topic;
protected @Nullable String command_topic;
protected @Nullable String state_value_template;
@SerializedName("state_topic")
protected @Nullable String stateTopic;
@SerializedName("command_topic")
protected @Nullable String commandTopic;
@SerializedName("state_value_template")
protected @Nullable String stateValueTemplate;
protected @Nullable String brightness_state_topic;
protected @Nullable String brightness_command_topic;
protected @Nullable String brightness_value_template;
@SerializedName("brightness_state_topic")
protected @Nullable String brightnessStateTopic;
@SerializedName("brightness_command_topic")
protected @Nullable String brightnessCommandTopic;
@SerializedName("brightness_value_template")
protected @Nullable String brightnessValueTemplate;
protected @Nullable String color_temp_state_topic;
protected @Nullable String color_temp_command_topic;
protected @Nullable String color_temp_value_template;
@SerializedName("color_temp_state_topic")
protected @Nullable String colorTempStateTopic;
@SerializedName("color_temp_command_topic")
protected @Nullable String colorTempCommandTopic;
@SerializedName("color_temp_value_template")
protected @Nullable String colorTempValueTemplate;
protected @Nullable String effect_command_topic;
protected @Nullable String effect_state_topic;
protected @Nullable String effect_value_template;
@SerializedName("effect_command_topic")
protected @Nullable String effectCommandTopic;
@SerializedName("effect_state_topic")
protected @Nullable String effectStateTopic;
@SerializedName("effect_value_template")
protected @Nullable String effectValueTemplate;
protected @Nullable String rgb_command_topic;
protected @Nullable String rgb_state_topic;
protected @Nullable String rgb_value_template;
protected @Nullable String rgb_command_template;
@SerializedName("rgb_command_topic")
protected @Nullable String rgbCommandTopic;
@SerializedName("rgb_state_topic")
protected @Nullable String rgbStateTopic;
@SerializedName("rgb_value_template")
protected @Nullable String rgbValueTemplate;
@SerializedName("rgb_command_template")
protected @Nullable String rgbCommandTemplate;
protected @Nullable String white_value_command_topic;
protected @Nullable String white_value_state_topic;
protected @Nullable String white_value_template;
@SerializedName("white_value_command_topic")
protected @Nullable String whiteValueCommandTopic;
@SerializedName("white_value_state_topic")
protected @Nullable String whiteValueStateTopic;
@SerializedName("white_value_template")
protected @Nullable String whiteValueTemplate;
protected @Nullable String xy_command_topic;
protected @Nullable String xy_state_topic;
protected @Nullable String xy_value_template;
@SerializedName("xy_command_topic")
protected @Nullable String xyCommandTopic;
@SerializedName("xy_state_topic")
protected @Nullable String xyStateTopic;
@SerializedName("xy_value_template")
protected @Nullable String xyValueTemplate;
protected String payload_on = "ON";
protected String payload_off = "OFF";
@SerializedName("payload_on")
protected String payloadOn = "ON";
@SerializedName("payload_off")
protected String payloadOff = "OFF";
}
protected ComponentChannel colorChannel;
@@ -102,30 +131,30 @@ public class Light extends AbstractComponent<Light.ChannelConfiguration> impleme
public Light(ComponentFactory.ComponentConfiguration builder) {
super(builder, ChannelConfiguration.class);
this.channelStateUpdateListener = builder.getUpdateListener();
ColorValue value = new ColorValue(ColorMode.RGB, channelConfiguration.payload_on,
channelConfiguration.payload_off, 100);
ColorValue value = new ColorValue(ColorMode.RGB, channelConfiguration.payloadOn,
channelConfiguration.payloadOff, 100);
// Create three MQTT subscriptions and use this class object as update listener
switchChannel = buildChannel(switchChannelID, value, channelConfiguration.getName(), this)
.stateTopic(channelConfiguration.state_topic, channelConfiguration.state_value_template,
switchChannel = buildChannel(SWITCH_CHANNEL_ID, value, channelConfiguration.getName(), this)
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.stateValueTemplate,
channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.command_topic, channelConfiguration.isRetain(),
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos())
.build(false);
colorChannel = buildChannel(colorChannelID, value, channelConfiguration.getName(), this)
.stateTopic(channelConfiguration.rgb_state_topic, channelConfiguration.rgb_value_template)
.commandTopic(channelConfiguration.rgb_command_topic, channelConfiguration.isRetain(),
colorChannel = buildChannel(COLOR_CHANNEL_ID, value, channelConfiguration.getName(), this)
.stateTopic(channelConfiguration.rgbStateTopic, channelConfiguration.rgbValueTemplate)
.commandTopic(channelConfiguration.rgbCommandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos())
.build(false);
brightnessChannel = buildChannel(brightnessChannelID, value, channelConfiguration.getName(), this)
.stateTopic(channelConfiguration.brightness_state_topic, channelConfiguration.brightness_value_template)
.commandTopic(channelConfiguration.brightness_command_topic, channelConfiguration.isRetain(),
brightnessChannel = buildChannel(BRIGHTNESS_CHANNEL_ID, value, channelConfiguration.getName(), this)
.stateTopic(channelConfiguration.brightnessStateTopic, channelConfiguration.brightnessValueTemplate)
.commandTopic(channelConfiguration.brightnessCommandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos())
.build(false);
channels.put(colorChannelID, colorChannel);
channels.put(COLOR_CHANNEL_ID, colorChannel);
}
@Override

View File

@@ -17,6 +17,8 @@ import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mqtt.generic.values.OnOffValue;
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
import com.google.gson.annotations.SerializedName;
/**
* A MQTT lock, following the https://www.home-assistant.io/components/lock.mqtt/ specification.
*
@@ -24,7 +26,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChanne
*/
@NonNullByDefault
public class Lock extends AbstractComponent<Lock.ChannelConfiguration> {
public static final String switchChannelID = "lock"; // Randomly chosen channel "ID"
public static final String SWITCH_CHANNEL_ID = "lock"; // Randomly chosen channel "ID"
/**
* Configuration class for MQTT component
@@ -36,25 +38,29 @@ public class Lock extends AbstractComponent<Lock.ChannelConfiguration> {
protected boolean optimistic = false;
protected String state_topic = "";
protected String payload_lock = "LOCK";
protected String payload_unlock = "UNLOCK";
protected @Nullable String command_topic;
@SerializedName("state_topic")
protected String stateTopic = "";
@SerializedName("payload_lock")
protected String payloadLock = "LOCK";
@SerializedName("payload_unlock")
protected String payloadUnlock = "UNLOCK";
@SerializedName("command_topic")
protected @Nullable String commandTopic;
}
public Lock(ComponentFactory.ComponentConfiguration componentConfiguration) {
super(componentConfiguration, ChannelConfiguration.class);
// We do not support all HomeAssistant quirks
if (channelConfiguration.optimistic && !channelConfiguration.state_topic.isBlank()) {
if (channelConfiguration.optimistic && !channelConfiguration.stateTopic.isBlank()) {
throw new UnsupportedOperationException("Component:Lock does not support forced optimistic mode");
}
buildChannel(switchChannelID,
new OnOffValue(channelConfiguration.payload_lock, channelConfiguration.payload_unlock),
buildChannel(SWITCH_CHANNEL_ID,
new OnOffValue(channelConfiguration.payloadLock, channelConfiguration.payloadUnlock),
channelConfiguration.getName(), componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.state_topic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.command_topic, channelConfiguration.isRetain(),
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos())
.build();
}

View File

@@ -24,6 +24,8 @@ import org.openhab.binding.mqtt.generic.values.Value;
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
import org.openhab.binding.mqtt.homeassistant.internal.listener.ExpireUpdateStateListener;
import com.google.gson.annotations.SerializedName;
/**
* A MQTT sensor, following the https://www.home-assistant.io/components/sensor.mqtt/ specification.
*
@@ -31,8 +33,8 @@ import org.openhab.binding.mqtt.homeassistant.internal.listener.ExpireUpdateStat
*/
@NonNullByDefault
public class Sensor extends AbstractComponent<Sensor.ChannelConfiguration> {
public static final String sensorChannelID = "sensor"; // Randomly chosen channel "ID"
private static final Pattern triggerIcons = Pattern.compile("^mdi:(toggle|gesture).*$");
public static final String SENSOR_CHANNEL_ID = "sensor"; // Randomly chosen channel "ID"
private static final Pattern TRIGGER_ICONS = Pattern.compile("^mdi:(toggle|gesture).*$");
/**
* Configuration class for MQTT component
@@ -42,23 +44,31 @@ public class Sensor extends AbstractComponent<Sensor.ChannelConfiguration> {
super("MQTT Sensor");
}
protected @Nullable String unit_of_measurement;
protected @Nullable String device_class;
protected boolean force_update = false;
protected @Nullable Integer expire_after;
@SerializedName("unit_of_measurement")
protected @Nullable String unitOfMeasurement;
@SerializedName("device_class")
protected @Nullable String deviceClass;
@SerializedName("force_update")
protected boolean forceUpdate = false;
@SerializedName("expire_after")
protected @Nullable Integer expireAfter;
protected String state_topic = "";
@SerializedName("state_topic")
protected String stateTopic = "";
protected @Nullable String json_attributes_topic;
protected @Nullable String json_attributes_template;
protected @Nullable List<String> json_attributes;
@SerializedName("json_attributes_topic")
protected @Nullable String jsonAttributesTopic;
@SerializedName("json_attributes_template")
protected @Nullable String jsonAttributesTemplate;
@SerializedName("json_attributes")
protected @Nullable List<String> jsonAttributes;
}
public Sensor(ComponentFactory.ComponentConfiguration componentConfiguration) {
super(componentConfiguration, ChannelConfiguration.class);
Value value;
String uom = channelConfiguration.unit_of_measurement;
String uom = channelConfiguration.unitOfMeasurement;
if (uom != null && !uom.isBlank()) {
value = new NumberValue(null, null, null, uom);
@@ -68,19 +78,20 @@ public class Sensor extends AbstractComponent<Sensor.ChannelConfiguration> {
String icon = channelConfiguration.getIcon();
boolean trigger = triggerIcons.matcher(icon).matches();
boolean trigger = TRIGGER_ICONS.matcher(icon).matches();
buildChannel(sensorChannelID, value, channelConfiguration.getName(), getListener(componentConfiguration, value))
.stateTopic(channelConfiguration.state_topic, channelConfiguration.getValueTemplate())//
.trigger(trigger).build();
buildChannel(SENSOR_CHANNEL_ID, value, channelConfiguration.getName(),
getListener(componentConfiguration, value))
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())//
.trigger(trigger).build();
}
private ChannelStateUpdateListener getListener(ComponentFactory.ComponentConfiguration componentConfiguration,
Value value) {
ChannelStateUpdateListener updateListener = componentConfiguration.getUpdateListener();
if (channelConfiguration.expire_after != null) {
updateListener = new ExpireUpdateStateListener(updateListener, channelConfiguration.expire_after, value,
if (channelConfiguration.expireAfter != null) {
updateListener = new ExpireUpdateStateListener(updateListener, channelConfiguration.expireAfter, value,
componentConfiguration.getTracker(), componentConfiguration.getScheduler());
}
return updateListener;

View File

@@ -17,6 +17,8 @@ import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mqtt.generic.values.OnOffValue;
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration;
import com.google.gson.annotations.SerializedName;
/**
* A MQTT switch, following the https://www.home-assistant.io/components/switch.mqtt/ specification.
*
@@ -24,7 +26,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChanne
*/
@NonNullByDefault
public class Switch extends AbstractComponent<Switch.ChannelConfiguration> {
public static final String switchChannelID = "switch"; // Randomly chosen channel "ID"
public static final String SWITCH_CHANNEL_ID = "switch"; // Randomly chosen channel "ID"
/**
* Configuration class for MQTT component
@@ -36,39 +38,47 @@ public class Switch extends AbstractComponent<Switch.ChannelConfiguration> {
protected @Nullable Boolean optimistic;
protected @Nullable String command_topic;
protected String state_topic = "";
@SerializedName("command_topic")
protected @Nullable String commandTopic;
@SerializedName("state_topic")
protected String stateTopic = "";
protected @Nullable String state_on;
protected @Nullable String state_off;
protected String payload_on = "ON";
protected String payload_off = "OFF";
@SerializedName("state_on")
protected @Nullable String stateOn;
@SerializedName("state_off")
protected @Nullable String stateOff;
@SerializedName("payload_on")
protected String payloadOn = "ON";
@SerializedName("payload_off")
protected String payloadOff = "OFF";
protected @Nullable String json_attributes_topic;
protected @Nullable String json_attributes_template;
@SerializedName("json_attributes_topic")
protected @Nullable String jsonAttributesTopic;
@SerializedName("json_attributes_template")
protected @Nullable String jsonAttributesTemplate;
}
public Switch(ComponentFactory.ComponentConfiguration componentConfiguration) {
super(componentConfiguration, ChannelConfiguration.class);
boolean optimistic = channelConfiguration.optimistic != null ? channelConfiguration.optimistic
: channelConfiguration.state_topic.isBlank();
: channelConfiguration.stateTopic.isBlank();
if (optimistic && !channelConfiguration.state_topic.isBlank()) {
if (optimistic && !channelConfiguration.stateTopic.isBlank()) {
throw new UnsupportedOperationException("Component:Switch does not support forced optimistic mode");
}
String state_on = channelConfiguration.state_on != null ? channelConfiguration.state_on
: channelConfiguration.payload_on;
String state_off = channelConfiguration.state_off != null ? channelConfiguration.state_off
: channelConfiguration.payload_off;
String stateOn = channelConfiguration.stateOn != null ? channelConfiguration.stateOn
: channelConfiguration.payloadOn;
String stateOff = channelConfiguration.stateOff != null ? channelConfiguration.stateOff
: channelConfiguration.payloadOff;
OnOffValue value = new OnOffValue(state_on, state_off, channelConfiguration.payload_on,
channelConfiguration.payload_off);
OnOffValue value = new OnOffValue(stateOn, stateOff, channelConfiguration.payloadOn,
channelConfiguration.payloadOff);
buildChannel(switchChannelID, value, "state", componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.state_topic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.command_topic, channelConfiguration.isRetain(),
buildChannel(SWITCH_CHANNEL_ID, value, "state", componentConfiguration.getUpdateListener())
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
channelConfiguration.getQos())
.build();
}

View File

@@ -14,6 +14,7 @@ package org.openhab.binding.mqtt.homeassistant.internal.config;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@@ -24,6 +25,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.config.dto.Device;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
@@ -37,7 +39,7 @@ import com.google.gson.stream.JsonWriter;
* that abbreviated names are replaces with their long versions during the read.
*
* <p>
* In elements, whose name end in'_topic' '~' replacement is performed.
* In elements, whose JSON name end in'_topic' '~' replacement is performed.
*
* <p>
* The adapters also handle {@link Device}
@@ -46,6 +48,7 @@ import com.google.gson.stream.JsonWriter;
*/
@NonNullByDefault
public class ChannelConfigurationTypeAdapterFactory implements TypeAdapterFactory {
private static final String MQTT_TOPIC_FIELD_SUFFIX = "_topic";
@Override
@Nullable
@@ -113,35 +116,45 @@ public class ChannelConfigurationTypeAdapterFactory implements TypeAdapterFactor
private void expandTidleInTopics(AbstractChannelConfiguration config) {
Class<?> type = config.getClass();
String tilde = config.getTilde();
String parentTopic = config.getParentTopic();
while (type != Object.class) {
Field[] fields = type.getDeclaredFields();
for (Field field : fields) {
if (String.class.isAssignableFrom(field.getType()) && field.getName().endsWith("_topic")) {
field.setAccessible(true);
try {
final String oldValue = (String) field.get(config);
String newValue = oldValue;
if (oldValue != null && !oldValue.isBlank()) {
if (oldValue.charAt(0) == '~') {
newValue = tilde + oldValue.substring(1);
} else if (oldValue.charAt(oldValue.length() - 1) == '~') {
newValue = oldValue.substring(0, oldValue.length() - 1) + tilde;
}
}
field.set(config, newValue);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
Arrays.stream(type.getDeclaredFields()).filter(this::isMqttTopicField)
.forEach(field -> replacePlaceholderByParentTopic(config, field, parentTopic));
type = type.getSuperclass();
}
}
private boolean isMqttTopicField(Field field) {
if (String.class.isAssignableFrom(field.getType())) {
final var serializedNameAnnotation = field.getAnnotation(SerializedName.class);
if (serializedNameAnnotation != null && serializedNameAnnotation.value() != null
&& serializedNameAnnotation.value().endsWith(MQTT_TOPIC_FIELD_SUFFIX)) {
return true;
}
}
return false;
}
private void replacePlaceholderByParentTopic(AbstractChannelConfiguration config, Field field, String parentTopic) {
field.setAccessible(true);
try {
final String oldValue = (String) field.get(config);
String newValue = oldValue;
if (oldValue != null && !oldValue.isBlank()) {
if (oldValue.charAt(0) == AbstractChannelConfiguration.PARENT_TOPIC_PLACEHOLDER) {
newValue = parentTopic + oldValue.substring(1);
} else if (oldValue
.charAt(oldValue.length() - 1) == AbstractChannelConfiguration.PARENT_TOPIC_PLACEHOLDER) {
newValue = oldValue.substring(0, oldValue.length() - 1) + parentTopic;
}
}
field.set(config, newValue);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -14,6 +14,8 @@ package org.openhab.binding.mqtt.homeassistant.internal.config;
import java.lang.reflect.Type;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mqtt.homeassistant.internal.config.dto.Connection;
import com.google.gson.JsonArray;
@@ -29,11 +31,23 @@ import com.google.gson.JsonParseException;
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class ConnectionDeserializer implements JsonDeserializer<Connection> {
@Override
public Connection deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
public @Nullable Connection deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
JsonArray list = json.getAsJsonArray();
JsonArray list;
if (json == null) {
throw new JsonParseException("JSON element is null");
}
try {
list = json.getAsJsonArray();
} catch (IllegalStateException e) {
throw new JsonParseException("Cannot parse JSON array", e);
}
if (list.size() != 2) {
throw new JsonParseException(
"Connection information must be a tuple, but has " + list.size() + " elements!");
}
return new Connection(list.get(0).getAsString(), list.get(1).getAsString());
}
}

View File

@@ -18,7 +18,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.TypeAdapter;
@@ -33,6 +33,7 @@ import com.google.gson.stream.JsonWriter;
*
* @author Jochen Klein - Initial contribution
*/
@NonNullByDefault
public class ListOrStringDeserializer extends TypeAdapter<List<String>> {
@Override
@@ -70,7 +71,7 @@ public class ListOrStringDeserializer extends TypeAdapter<List<String>> {
}
}
private @NonNull List<String> readList(@NonNull JsonReader in) throws IOException {
private List<String> readList(JsonReader in) throws IOException {
in.beginArray();
List<String> result = new ArrayList<>();

View File

@@ -31,18 +31,26 @@ import com.google.gson.annotations.SerializedName;
*/
@NonNullByDefault
public abstract class AbstractChannelConfiguration {
public static final char PARENT_TOPIC_PLACEHOLDER = '~';
protected String name;
protected String icon = "";
protected int qos; // defaults to 0 according to HA specification
protected boolean retain; // defaults to false according to HA specification
protected @Nullable String value_template;
protected @Nullable String unique_id;
@SerializedName("value_template")
protected @Nullable String valueTemplate;
@SerializedName("unique_id")
protected @Nullable String uniqueId;
protected AvailabilityMode availability_mode = AvailabilityMode.LATEST;
protected @Nullable String availability_topic;
protected String payload_available = "online";
protected String payload_not_available = "offline";
@SerializedName("availability_mode")
protected AvailabilityMode availabilityMode = AvailabilityMode.LATEST;
@SerializedName("availability_topic")
protected @Nullable String availabilityTopic;
@SerializedName("payload_available")
protected String payloadAvailable = "online";
@SerializedName("payload_not_available")
protected String payloadNotAvailable = "offline";
/**
* A list of MQTT topics subscribed to receive availability (online/offline) updates. Must not be used together with
@@ -51,7 +59,7 @@ public abstract class AbstractChannelConfiguration {
protected @Nullable List<Availability> availability;
@SerializedName(value = "~")
protected String tilde = "";
protected String parentTopic = "";
protected @Nullable Device device;
@@ -70,10 +78,6 @@ public abstract class AbstractChannelConfiguration {
this.name = defaultName;
}
public @Nullable String expand(@Nullable String value) {
return value == null ? null : value.replaceAll("~", tilde);
}
public String getThingName() {
String result = null;
@@ -92,27 +96,27 @@ public abstract class AbstractChannelConfiguration {
result = this.device.getId();
}
if (result == null) {
result = unique_id;
result = uniqueId;
}
return UIDUtils.encode(result != null ? result : defaultId);
}
public Map<String, Object> appendToProperties(Map<String, Object> properties) {
final Device device_ = device;
if (device_ == null) {
final Device d = device;
if (d == null) {
return properties;
}
final String manufacturer = device_.manufacturer;
final String manufacturer = d.manufacturer;
if (manufacturer != null) {
properties.put(Thing.PROPERTY_VENDOR, manufacturer);
}
final String model = device_.model;
final String model = d.model;
if (model != null) {
properties.put(Thing.PROPERTY_MODEL_ID, model);
}
final String sw_version = device_.swVersion;
if (sw_version != null) {
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, sw_version);
final String swVersion = d.swVersion;
if (swVersion != null) {
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, swVersion);
}
return properties;
}
@@ -135,25 +139,25 @@ public abstract class AbstractChannelConfiguration {
@Nullable
public String getValueTemplate() {
return value_template;
return valueTemplate;
}
@Nullable
public String getUniqueId() {
return unique_id;
return uniqueId;
}
@Nullable
public String getAvailabilityTopic() {
return availability_topic;
return availabilityTopic;
}
public String getPayloadAvailable() {
return payload_available;
return payloadAvailable;
}
public String getPayloadNotAvailable() {
return payload_not_available;
return payloadNotAvailable;
}
@Nullable
@@ -166,12 +170,12 @@ public abstract class AbstractChannelConfiguration {
return availability;
}
public String getTilde() {
return tilde;
public String getParentTopic() {
return parentTopic;
}
public AvailabilityMode getAvailabilityMode() {
return availability_mode;
return availabilityMode;
}
/**

View File

@@ -12,6 +12,8 @@
*/
package org.openhab.binding.mqtt.homeassistant.internal.config.dto;
import com.google.gson.annotations.SerializedName;
/**
* MQTT topic subscribed to receive availability (online/offline) updates. Must not be used together with
* availability_topic
@@ -19,16 +21,18 @@ package org.openhab.binding.mqtt.homeassistant.internal.config.dto;
* @author Anton Kharuzhy - Initial contribution
*/
public class Availability {
protected String payload_available = "online";
protected String payload_not_available = "offline";
@SerializedName("payload_available")
protected String payloadAvailable = "online";
@SerializedName("payload_not_available")
protected String payloadNotAvailable = "offline";
protected String topic;
public String getPayload_available() {
return payload_available;
public String getPayloadAvailable() {
return payloadAvailable;
}
public String getPayload_not_available() {
return payload_not_available;
public String getPayloadNotAvailable() {
return payloadNotAvailable;
}
public String getTopic() {

View File

@@ -26,7 +26,6 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.mqtt.discovery.AbstractMQTTDiscovery;
@@ -122,7 +121,7 @@ public class HomeAssistantDiscovery extends AbstractMQTTDiscovery {
}
@Override
public Set<@NonNull ThingTypeUID> getSupportedThingTypes() {
public Set<ThingTypeUID> getSupportedThingTypes() {
return typeProvider.getThingTypeUIDs();
}

View File

@@ -12,7 +12,6 @@
*/
package org.openhab.binding.mqtt.homeassistant.internal.listener;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener;
import org.openhab.core.thing.ChannelUID;
@@ -36,17 +35,17 @@ public abstract class ChannelStateUpdateListenerProxy implements ChannelStateUpd
}
@Override
public void updateChannelState(@NonNull ChannelUID channelUID, @NonNull State value) {
public void updateChannelState(ChannelUID channelUID, State value) {
original.updateChannelState(channelUID, value);
}
@Override
public void postChannelCommand(@NonNull ChannelUID channelUID, @NonNull Command value) {
public void postChannelCommand(ChannelUID channelUID, Command value) {
original.postChannelCommand(channelUID, value);
}
@Override
public void triggerChannel(@NonNull ChannelUID channelUID, @NonNull String eventPayload) {
public void triggerChannel(ChannelUID channelUID, String eventPayload) {
original.triggerChannel(channelUID, eventPayload);
}
}