2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.airgradient.internal.discovery;
15 import static org.openhab.binding.airgradient.internal.AirGradientBindingConstants.BACKGROUND_DISCOVERY;
16 import static org.openhab.binding.airgradient.internal.AirGradientBindingConstants.CONFIG_LOCATION;
17 import static org.openhab.binding.airgradient.internal.AirGradientBindingConstants.PROPERTY_NAME;
18 import static org.openhab.binding.airgradient.internal.AirGradientBindingConstants.SEARCH_TIME;
19 import static org.openhab.binding.airgradient.internal.AirGradientBindingConstants.THING_TYPE_LOCATION;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.List;
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.openhab.binding.airgradient.internal.communication.AirGradientCommunicationException;
30 import org.openhab.binding.airgradient.internal.handler.AirGradientAPIHandler;
31 import org.openhab.binding.airgradient.internal.handler.PollEventListener;
32 import org.openhab.binding.airgradient.internal.model.Measure;
33 import org.openhab.core.config.discovery.AbstractDiscoveryService;
34 import org.openhab.core.config.discovery.DiscoveryResult;
35 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingUID;
38 import org.openhab.core.thing.binding.BridgeHandler;
39 import org.openhab.core.thing.binding.ThingHandler;
40 import org.openhab.core.thing.binding.ThingHandlerService;
41 import org.osgi.service.component.annotations.Component;
42 import org.osgi.service.component.annotations.ServiceScope;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
47 * The {@link AirGradientLocationDiscoveryService} is responsible for discovering new locations
48 * that are not bound to any items.
50 * @author Jørgen Austvik - Initial contribution
52 @Component(scope = ServiceScope.PROTOTYPE, service = AirGradientLocationDiscoveryService.class)
54 public class AirGradientLocationDiscoveryService extends AbstractDiscoveryService
55 implements ThingHandlerService, PollEventListener {
57 private final Logger logger = LoggerFactory.getLogger(AirGradientLocationDiscoveryService.class);
59 private @NonNullByDefault({}) AirGradientAPIHandler apiHandler;
61 public AirGradientLocationDiscoveryService() {
62 super(Set.of(THING_TYPE_LOCATION), (int) SEARCH_TIME.getSeconds(), BACKGROUND_DISCOVERY);
66 protected void startBackgroundDiscovery() {
67 logger.debug("Start AirGradient background discovery");
68 apiHandler.addPollEventListener(this);
72 protected void stopBackgroundDiscovery() {
73 logger.debug("Stopping AirGradient background discovery");
74 apiHandler.removePollEventListener(this);
78 public void pollEvent(List<Measure> measures) {
79 BridgeHandler bridge = apiHandler.getThing().getHandler();
81 logger.debug("Missing bridge, can't discover sensors for unknown bridge.");
85 ThingUID bridgeUid = bridge.getThing().getUID();
87 Set<String> registeredLocationIds = new HashSet<>(apiHandler.getRegisteredLocationIds());
88 for (Measure measure : measures) {
89 String id = measure.getLocationId();
91 // Local devices don't have location ID.
92 id = measure.getSerialNo();
95 String name = measure.getLocationName();
97 name = "Sensor_" + measure.getSerialNo();
100 if (!registeredLocationIds.contains(id)) {
101 Map<String, Object> properties = new HashMap<>(5);
102 properties.put(PROPERTY_NAME, name);
103 properties.put(Thing.PROPERTY_FIRMWARE_VERSION, measure.getFirmwareVersion());
104 properties.put(Thing.PROPERTY_SERIAL_NUMBER, measure.getSerialNo());
105 String model = measure.getModel();
107 properties.put(Thing.PROPERTY_MODEL_ID, model);
109 properties.put(CONFIG_LOCATION, id);
111 ThingUID thingUID = new ThingUID(THING_TYPE_LOCATION, bridgeUid, id);
113 logger.debug("Adding location {} with id {} to bridge {} with location id {}", name, thingUID,
114 bridgeUid, measure.getLocationId());
115 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
116 .withBridge(bridgeUid).withLabel(name).withRepresentationProperty(CONFIG_LOCATION).build();
118 thingDiscovered(discoveryResult);
124 protected void startScan() {
126 List<Measure> measures = apiHandler.getApiController().getMeasures();
128 } catch (AirGradientCommunicationException agce) {
129 logger.warn("Failed discovery due to communication exception: {}", agce.getMessage());
134 public void setThingHandler(ThingHandler handler) {
135 if (handler instanceof AirGradientAPIHandler airGradientAPIHandler) {
136 this.apiHandler = airGradientAPIHandler;
141 public @Nullable ThingHandler getThingHandler() {
146 public void activate() {
147 super.activate(null);
151 public void deactivate() {