-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[boschshc] Update location properties when initializing things #17893
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -12,6 +12,10 @@ | |||||
*/ | ||||||
package org.openhab.binding.boschshc.internal.devices; | ||||||
|
||||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.PROPERTY_LOCATION; | ||||||
import static org.openhab.binding.boschshc.internal.devices.BoschSHCBindingConstants.PROPERTY_LOCATION_LEGACY; | ||||||
|
||||||
import java.util.Map; | ||||||
import java.util.concurrent.ExecutionException; | ||||||
import java.util.concurrent.TimeoutException; | ||||||
|
||||||
|
@@ -83,9 +87,44 @@ public void initialize() { | |||||
* otherwise | ||||||
*/ | ||||||
protected boolean processDeviceInfo(Device deviceInfo) { | ||||||
try { | ||||||
updateLocationPropertiesIfApplicable(deviceInfo); | ||||||
} catch (Exception e) { | ||||||
logger.warn("Error while updating location properties for thing {}.", getThing().getUID(), e); | ||||||
} | ||||||
// do not cancel thing initialization if location properties cannot be updated | ||||||
return true; | ||||||
} | ||||||
|
||||||
private void updateLocationPropertiesIfApplicable(Device deviceInfo) throws BoschSHCException { | ||||||
Map<String, String> thingProperties = getThing().getProperties(); | ||||||
removeLegacyLocationPropertyIfApplicable(thingProperties); | ||||||
updateLocationPropertyIfApplicable(thingProperties, deviceInfo); | ||||||
} | ||||||
|
||||||
private void updateLocationPropertyIfApplicable(Map<String, String> thingProperties, Device deviceInfo) | ||||||
throws BoschSHCException { | ||||||
@Nullable | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
String roomName = getBridgeHandler().resolveRoomId(deviceInfo.roomId); | ||||||
if (roomName != null) { | ||||||
@Nullable | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
String currentLocation = thingProperties.get(PROPERTY_LOCATION); | ||||||
if (currentLocation == null || !currentLocation.equals(roomName)) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can be simplified since
Suggested change
|
||||||
logger.debug("Updating property '{}' of thing {} to '{}'.", PROPERTY_LOCATION, getThing().getUID(), | ||||||
roomName); | ||||||
updateProperty(PROPERTY_LOCATION, roomName); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jlaur I have a general conceptual question on this change: we store the room names in which devices are located in a thing property, which used to have the non-compliant spelling My question is whether a thing property is the correct place to store this in general, because I looked at properties of other things and found that they are usually quite technical / hardware-specific. It this the right place to store location metadata, or do you have better ideas? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I forgot about this again after receiving an e-mail notification amongst many. Can you provide an example of a location, and perhaps more importantly, what is the use-case of having it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @jlaur, no problem, this PR is not time-critical, as it does not affect the actual thing/channel logics. It is kind of in between a bug and an enhancement 😉 I'm grateful for your support in any case. The Bosch Smart Home system allows users to manage their rooms and to assign devices to rooms. For example, users can specify that window sensor A ist located in the living room and that smoke detector B is located in the dining room. Internally, each room has a somewhat cryptic ID (like In rare cases, for example if devices are repurposed or moved, the room assignment might change. This is not reflected in openHAB so far. Behavior before this PR:
Behavior with this PR:
The question is whether a thing property is the right place to store room names at all. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I noticed I did not answer your second question. The use-case of having it is purely informational, there is no technical requirement to have location properties / room names and the location is not required in any thing/channel logics. In this case It might also be an option to just remove the property completely since it is technically not required at the moment. A possible future enhancement could be to expose rooms as openHAB things. If it becomes a requirement to model device <-> room associations, this would be done using device/room IDs, not the user-specified room names. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I agree. During the development of the thing discovery I added this room information to help the users. With the additional information it is easier to organize all the new discovered things. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In that case I think a property is the right approach. For sure it wouldn't make sense to expose as a channel. I can think of Hue and Netatmo also having room assignments. It would be cool if this could be somehow mapped to the semantic model of openHAB, but I don't think that's currently possible, and I don't have a concrete idea what could even be gained from that. 🙂 @lolodomo, tagging you in case you have any thoughts on this topic, otherwise feel free to ignore. In any case, this is a side-topic. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Btw, there is also a concept of a location of a Thing: https://www.openhab.org/docs/configuration/things.html#defining-things-using-files It can be set dynamically by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very good point @jlaur, this would indeed be a good alternative to store the location. However, as you mentioned, I am uncertain whether
It would be great if we could get some general conceptual input. Maybe @lolodomo has some ideas, or knows someone we could ask? |
||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
private void removeLegacyLocationPropertyIfApplicable(Map<String, String> thingProperties) { | ||||||
if (thingProperties.containsKey(PROPERTY_LOCATION_LEGACY)) { | ||||||
logger.debug("Removing legacy property '{}' from thing {}.", PROPERTY_LOCATION_LEGACY, getThing().getUID()); | ||||||
// null value indicates that the property should be removed | ||||||
updateProperty(PROPERTY_LOCATION_LEGACY, null); | ||||||
} | ||||||
} | ||||||
|
||||||
/** | ||||||
* Attempts to obtain information about the device with the specified ID via a REST call. | ||||||
* <p> | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
import static org.eclipse.jetty.http.HttpMethod.PUT; | ||
|
||
import java.lang.reflect.Type; | ||
import java.time.Duration; | ||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
|
@@ -54,6 +55,7 @@ | |
import org.openhab.binding.boschshc.internal.serialization.GsonUtils; | ||
import org.openhab.binding.boschshc.internal.services.dto.BoschSHCServiceState; | ||
import org.openhab.binding.boschshc.internal.services.dto.JsonRestExceptionResponse; | ||
import org.openhab.core.cache.ExpiringCache; | ||
import org.openhab.core.library.types.StringType; | ||
import org.openhab.core.thing.Bridge; | ||
import org.openhab.core.thing.Channel; | ||
|
@@ -88,6 +90,8 @@ public class BridgeHandler extends BaseBridgeHandler { | |
|
||
private static final String HTTP_CLIENT_NOT_INITIALIZED = "HttpClient not initialized"; | ||
|
||
private static final Duration ROOM_CACHE_DURATION = Duration.ofMinutes(2); | ||
|
||
private final Logger logger = LoggerFactory.getLogger(BridgeHandler.class); | ||
|
||
/** | ||
|
@@ -107,13 +111,22 @@ public class BridgeHandler extends BaseBridgeHandler { | |
|
||
/** | ||
* SHC thing/device discovery service instance. | ||
* Registered and unregistered if service is actived/deactived. | ||
* Registered and unregistered if service is activated/deactivated. | ||
* Used to scan for things after bridge is paired with SHC. | ||
*/ | ||
private @Nullable ThingDiscoveryService thingDiscoveryService; | ||
|
||
private final ScenarioHandler scenarioHandler; | ||
|
||
private ExpiringCache<List<Room>> roomCache = new ExpiringCache<>(ROOM_CACHE_DURATION, () -> { | ||
try { | ||
return getRooms(); | ||
} catch (InterruptedException e) { | ||
Thread.currentThread().interrupt(); | ||
return null; | ||
} | ||
}); | ||
|
||
public BridgeHandler(Bridge bridge) { | ||
super(bridge); | ||
scenarioHandler = new ScenarioHandler(); | ||
|
@@ -437,6 +450,24 @@ public List<Room> getRooms() throws InterruptedException { | |
} | ||
} | ||
|
||
public @Nullable List<Room> getRoomsWithCache() { | ||
return roomCache.getValue(); | ||
} | ||
|
||
public @Nullable String resolveRoomId(@Nullable String roomId) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @david-pace, do you think a unit tests for the new public function could make sense? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right, it would make sense to add more unit tests for these new methods. I will add them as soon as I find some time. |
||
if (roomId == null) { | ||
return null; | ||
} | ||
|
||
@Nullable | ||
List<Room> rooms = getRoomsWithCache(); | ||
if (rooms != null) { | ||
return rooms.stream().filter(r -> r.id.equals(roomId)).map(r -> r.name).findAny().orElse(null); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
/** | ||
* Get public information from Bosch SHC. | ||
*/ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would be sufficient?
otherwise you could hide bugs in case a
RuntimeException
is thrown.