[mqtt.espmilighthub] use availability topic to set thing status (#13800)

also refactor a bit so that the thing will work even if the bridge
gets added and removed, etc.

Signed-off-by: Cody Cutrer <cody@cutrer.us>
This commit is contained in:
Cody Cutrer 2023-03-24 17:52:37 -06:00 committed by GitHub
parent 3b08217ff7
commit 2088cf067f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 69 additions and 63 deletions

View File

@ -31,6 +31,7 @@ import org.openhab.core.thing.ThingTypeUID;
public class EspMilightHubBindingConstants {
public static final String STATES_BASE_TOPIC = "milight/states/";
public static final String COMMANDS_BASE_TOPIC = "milight/commands/";
public static final String STATUS_TOPIC = "milight/status";
public static final BigDecimal BIG_DECIMAL_100 = new BigDecimal(100);
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_RGB_CCT = new ThingTypeUID(BINDING_ID, "rgb_cct");
@ -50,4 +51,7 @@ public class EspMilightHubBindingConstants {
public static final String CHANNEL_DISCO_MODE = "discoMode";
public static final String CHANNEL_BULB_MODE = "bulbMode";
public static final String CHANNEL_COMMAND = "command";
// Status
public static final String CONNECTED = "connected";
}

View File

@ -19,6 +19,9 @@ import static org.openhab.binding.mqtt.espmilighthub.internal.EspMilightHubBindi
import java.math.BigDecimal;
import java.math.MathContext;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@ -26,8 +29,6 @@ import org.openhab.binding.mqtt.espmilighthub.internal.ConfigOptions;
import org.openhab.binding.mqtt.espmilighthub.internal.Helper;
import org.openhab.binding.mqtt.handler.AbstractBrokerHandler;
import org.openhab.core.io.transport.mqtt.MqttBrokerConnection;
import org.openhab.core.io.transport.mqtt.MqttConnectionObserver;
import org.openhab.core.io.transport.mqtt.MqttConnectionState;
import org.openhab.core.io.transport.mqtt.MqttMessageSubscriber;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType;
@ -41,7 +42,7 @@ import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
@ -57,7 +58,7 @@ import org.slf4j.LoggerFactory;
* @author Matthew Skinner - Initial contribution
*/
@NonNullByDefault
public class EspMilightHubHandler extends BaseThingHandler implements MqttConnectionObserver, MqttMessageSubscriber {
public class EspMilightHubHandler extends BaseThingHandler implements MqttMessageSubscriber {
// these are all constants used in color conversion calcuations.
// strings are necessary to prevent floating point loss of precision
private static final BigDecimal BIG_DECIMAL_THOUSAND = new BigDecimal(1000);
@ -451,29 +452,22 @@ public class EspMilightHubHandler extends BaseThingHandler implements MqttConnec
return;
}
}
Bridge localBridge = getBridge();
if (localBridge == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING,
"Globe must have a valid bridge selected before it can come online.");
return;
} else {
globeType = thing.getThingTypeUID().getId();// eg rgb_cct
String globeLocation = this.getThing().getUID().getId();// eg 0x014
remotesGroupID = globeLocation.substring(globeLocation.length() - 1, globeLocation.length());// eg 4
String remotesIDCode = globeLocation.substring(0, globeLocation.length() - 1);// eg 0x01
fullCommandTopic = COMMANDS_BASE_TOPIC + remotesIDCode + "/" + globeType + "/" + remotesGroupID;
fullStatesTopic = STATES_BASE_TOPIC + remotesIDCode + "/" + globeType + "/" + remotesGroupID;
// Need to remove the lowercase x from 0x12AB in case it contains all numbers
String caseCheck = globeLocation.substring(2, globeLocation.length() - 1);
if (!caseCheck.equals(caseCheck.toUpperCase())) {
logger.warn(
"The milight globe {}{} is using lowercase for the remote code when the hub needs UPPERCASE",
remotesIDCode, remotesGroupID);
}
channelPrefix = BINDING_ID + ":" + globeType + ":" + localBridge.getUID().getId() + ":" + remotesIDCode
+ remotesGroupID + ":";
connectMQTT();
globeType = thing.getThingTypeUID().getId();// eg rgb_cct
String globeLocation = this.getThing().getUID().getId();// eg 0x014
remotesGroupID = globeLocation.substring(globeLocation.length() - 1, globeLocation.length());// eg 4
String remotesIDCode = globeLocation.substring(0, globeLocation.length() - 1);// eg 0x01
fullCommandTopic = COMMANDS_BASE_TOPIC + remotesIDCode + "/" + globeType + "/" + remotesGroupID;
fullStatesTopic = STATES_BASE_TOPIC + remotesIDCode + "/" + globeType + "/" + remotesGroupID;
// Need to remove the lowercase x from 0x12AB in case it contains all numbers
String caseCheck = globeLocation.substring(2, globeLocation.length() - 1);
if (!caseCheck.equals(caseCheck.toUpperCase())) {
logger.warn("The milight globe {}{} is using lowercase for the remote code when the hub needs UPPERCASE",
remotesIDCode, remotesGroupID);
}
channelPrefix = BINDING_ID + ":" + globeType + ":" + thing.getBridgeUID().getId() + ":" + remotesIDCode
+ remotesGroupID + ":";
bridgeStatusChanged(getBridgeStatus());
}
private void sendMQTT(String payload) {
@ -487,57 +481,64 @@ public class EspMilightHubHandler extends BaseThingHandler implements MqttConnec
public void processMessage(String topic, byte[] payload) {
String state = new String(payload, StandardCharsets.UTF_8);
logger.trace("Received the following new Milight state:{}:{}", topic, state);
try {
processIncomingState(state);
} catch (Exception e) {
logger.warn("Failed processing Milight state {} for {}", state, topic, e);
if (topic.equals(STATUS_TOPIC)) {
if (state.equals(CONNECTED)) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Milight Hub is not connected to your MQTT broker.");
}
} else {
try {
processIncomingState(state);
} catch (Exception e) {
logger.warn("Failed processing Milight state {} for {}", state, topic, e);
}
}
}
public ThingStatusInfo getBridgeStatus() {
Bridge b = getBridge();
if (b != null) {
return b.getStatusInfo();
} else {
return new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, null);
}
}
@Override
public void connectionStateChanged(MqttConnectionState state, @Nullable Throwable error) {
logger.debug("MQTT brokers state changed to:{}", state);
switch (state) {
case CONNECTED:
updateStatus(ThingStatus.ONLINE);
break;
case CONNECTING:
case DISCONNECTED:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Bridge (broker) is not connected to your MQTT broker.");
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
connection = null;
return;
}
if (bridgeStatusInfo.getStatus() != ThingStatus.ONLINE) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
return;
}
}
public void connectMQTT() {
Bridge localBridge = this.getBridge();
if (localBridge == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
"Bridge is missing or offline, you need to setup a working MQTT broker first.");
return;
}
ThingUID thingUID = localBridge.getUID();
Thing thing = thingRegistry.get(thingUID);
if (thing == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
"Bridge is missing or offline, you need to setup a working MQTT broker first.");
return;
}
ThingHandler handler = thing.getHandler();
ThingHandler handler = localBridge.getHandler();
if (handler instanceof AbstractBrokerHandler) {
AbstractBrokerHandler abh = (AbstractBrokerHandler) handler;
MqttBrokerConnection localConnection = abh.getConnection();
if (localConnection != null) {
localConnection.setKeepAliveInterval(20);
localConnection.setQos(1);
localConnection.setUnsubscribeOnStop(true);
localConnection.addConnectionObserver(this);
localConnection.start();
localConnection.subscribe(fullStatesTopic + "/#", this);
connection = localConnection;
if (localConnection.connectionState().compareTo(MqttConnectionState.CONNECTED) == 0) {
updateStatus(ThingStatus.ONLINE);
}
final MqttBrokerConnection connection;
try {
connection = abh.getConnectionAsync().get(500, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException ignored) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
"Bridge handler has no valid broker connection!");
return;
}
this.connection = connection;
connection.subscribe(fullStatesTopic, this);
connection.subscribe(STATUS_TOPIC, this);
}
return;
}
@ -546,7 +547,8 @@ public class EspMilightHubHandler extends BaseThingHandler implements MqttConnec
public void dispose() {
MqttBrokerConnection localConnection = connection;
if (localConnection != null) {
localConnection.unsubscribe(fullStatesTopic + "/#", this);
localConnection.unsubscribe(fullStatesTopic, this);
localConnection.unsubscribe(STATUS_TOPIC, this);
}
}
}