use of org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homeassistant.HaID in project smarthome by eclipse.
the class HomeAssistantMQTTImplementationTests method parseHATree.
@Test
public void parseHATree() throws InterruptedException, ExecutionException, TimeoutException {
MqttChannelTypeProvider channelTypeProvider = mock(MqttChannelTypeProvider.class);
final Map<String, AbstractComponent> haComponents = new HashMap<String, AbstractComponent>();
ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(4);
DiscoverComponents discover = spy(new DiscoverComponents(ThingChannelConstants.testHomeAssistantThing, scheduler, channelStateUpdateListener, new Gson()));
// The DiscoverComponents object calls ComponentDiscovered callbacks.
// In the following implementation we add the found component to the `haComponents` map
// and add the types to the channelTypeProvider, like in the real Thing handler.
final CountDownLatch latch = new CountDownLatch(1);
ComponentDiscovered cd = (haID, c) -> {
haComponents.put(haID.getChannelGroupID(), c);
c.addChannelTypes(channelTypeProvider);
channelTypeProvider.setChannelGroupType(c.groupTypeUID(), c.type());
latch.countDown();
};
// Start the discovery for 100ms. Forced timeout after 300ms.
HaID haID = new HaID(testObjectTopic);
CompletableFuture<Void> future = discover.startDiscovery(connection, 100, haID, cd).thenRun(() -> {
}).exceptionally(e -> {
failure = e;
return null;
});
assertTrue(latch.await(300, TimeUnit.MILLISECONDS));
future.get(100, TimeUnit.MILLISECONDS);
// No failure expected and one discoverd result
assertNull(failure);
assertThat(haComponents.size(), is(1));
// For the switch component we should have one channel group type and one channel type
verify(channelTypeProvider, times(1)).setChannelGroupType(any(), any());
verify(channelTypeProvider, times(1)).setChannelType(any(), any());
// We expect a switch component with an OnOff channel with the initial value UNDEF:
State value = haComponents.get(haID.getChannelGroupID()).channelTypes().get(ComponentSwitch.switchChannelID).channelState.getCache().getChannelState();
assertThat(value, is(UnDefType.UNDEF));
haComponents.values().stream().map(e -> e.start(connection, scheduler, 100)).reduce(CompletableFuture.completedFuture(null), (a, v) -> a.thenCompose(b -> v)).exceptionally(e -> {
failure = e;
return null;
}).get();
// We should have received the retained value, while subscribing to the channels MQTT state topic.
verify(channelStateUpdateListener, times(1)).updateChannelState(any(), any());
// Value should be ON now.
value = haComponents.get(haID.getChannelGroupID()).channelTypes().get(ComponentSwitch.switchChannelID).channelState.getCache().getChannelState();
assertThat(value, is(OnOffType.ON));
}
use of org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homeassistant.HaID in project smarthome by eclipse.
the class HomeAssistantDiscovery method receivedMessage.
@Override
public void receivedMessage(ThingUID connectionBridge, MqttBrokerConnection connection, String topic, byte[] payload) {
// We check for the last part to filter all non-config topics out.
if (!topic.endsWith("/config")) {
return;
}
// We will of course find multiple of the same unique Thing IDs, for each different component another one.
// Therefore the components are assembled into a list and given to the DiscoveryResult label for the user to
// easily recognise object capabilities.
HaID topicParts = determineTopicParts(topic);
final String thingID = topicParts.getThingID();
final ThingUID thingUID = new ThingUID(MqttBindingConstants.HOMEASSISTANT_MQTT_THING, connectionBridge, thingID);
// Reset the found-component timer.
// We will collect components for the thing label description for another 2 seconds.
final ScheduledFuture<?> future = this.future;
if (future != null) {
future.cancel(false);
}
this.future = scheduler.schedule(componentsPerThingID::clear, 2, TimeUnit.SECONDS);
// We need to keep track of already found component topics for a specific object_id/node_id
Set<String> components = componentsPerThingID.getOrDefault(thingID, new HashSet<>());
if (components.contains(topicParts.component)) {
logger.trace("Discovered an already known component {}", topicParts.component);
// If we already know about this object component, ignore the discovered topic.
return;
}
components.add(topicParts.component);
componentsPerThingID.put(thingID, components);
final String componentNames = components.stream().map(c -> HA_COMP_TO_NAME.getOrDefault(c, c)).collect(Collectors.joining(","));
Config config = new Gson().fromJson(new String(payload, StandardCharsets.UTF_8), Config.class);
Map<String, Object> properties = new HashMap<>();
properties.put("objectid", topicParts.objectID);
properties.put("nodeid", topicParts.nodeID);
properties.put("basetopic", BASE_TOPIC);
// First remove an already discovered thing with the same ID
thingRemoved(thingUID);
// Because we need the new properties map with the updated "components" list
thingDiscovered(DiscoveryResultBuilder.create(thingUID).withProperties(properties).withRepresentationProperty("objectid").withBridge(connectionBridge).withLabel(config.name + " (" + componentNames + ")").build());
}
use of org.eclipse.smarthome.binding.mqtt.generic.internal.convention.homeassistant.HaID in project smarthome by eclipse.
the class HomeAssistantThingHandler method initialize.
@SuppressWarnings({ "null", "unused" })
@Override
public void initialize() {
config = getConfigAs(HandlerConfiguration.class);
if (config.objectid.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Device ID unknown");
return;
}
discoveryHomeAssistantID = new HaID(config.basetopic, config.objectid, "", "");
for (Channel channel : thing.getChannels()) {
final String groupID = channel.getUID().getGroupId();
if (groupID == null) {
logger.warn("Channel {} has no groupd ID", channel.getLabel());
continue;
}
// Already restored component?
@Nullable AbstractComponent component = haComponents.get(groupID);
if (component != null) {
continue;
} else {
component = CFactory.createComponent(config.basetopic, channel, this, gson);
}
if (component != null) {
haComponents.put(component.uid().getId(), component);
component.addChannelTypes(channelTypeProvider);
} else {
logger.warn("Could not restore component {}", thing);
}
}
super.initialize();
}
Aggregations