}
/**
- * The model name provided by upnp is formated like in the example form "Sonos PLAY:1" or "Sonos PLAYBAR"
+ * Build a valid thing type ID from the model name provided by UPnP
*
- * @param sonosModelName Sonos model name provided via upnp device
- * @return the extracted players model name without column (:) character used for ThingType creation
+ * @param sonosModelName Sonos model name provided via UPnP device
+ * @return a valid thing type ID that can then be used for ThingType creation
*/
- public static String extractModelName(String sonosModelName) {
- String ret = sonosModelName;
- Matcher matcher = Pattern.compile("\\s(.*)").matcher(ret);
+ public static String buildThingTypeIdFromModelName(String sonosModelName) {
+ // For Ikea SYMFONISK models, the model name now starts with "SYMFONISK" with recent firmwares
+ if (sonosModelName.toUpperCase().contains("SYMFONISK")) {
+ return "SYMFONISK";
+ }
+ String id = sonosModelName;
+ // Remove until the first space (in practice, it removes the leading "Sonos " from the model name)
+ Matcher matcher = Pattern.compile("\\s(.*)").matcher(id);
if (matcher.find()) {
- ret = matcher.group(1);
+ id = matcher.group(1);
+ // Remove a potential ending text surrounded with parenthesis
+ matcher = Pattern.compile("(.*)\\s\\(.*\\)").matcher(id);
+ if (matcher.find()) {
+ id = matcher.group(1);
+ }
}
- if (ret.contains(":")) {
- ret = ret.replace(":", "");
+ // Finally remove unexpected characters in a thing type ID
+ id = id.replaceAll("[^a-zA-Z0-9_]", "");
+ // ZP80 is translated to CONNECT and ZP100 to CONNECTAMP
+ switch (id) {
+ case "ZP80":
+ id = "CONNECT";
+ break;
+ case "ZP100":
+ id = "CONNECTAMP";
+ break;
+ default:
+ break;
}
- return ret;
+ return id;
}
public static String compileMetadataString(SonosEntry entry) {
public @Nullable ThingUID getThingUID(RemoteDevice device) {
if (device.getDetails().getManufacturerDetails().getManufacturer() != null) {
if (device.getDetails().getManufacturerDetails().getManufacturer().toUpperCase().contains("SONOS")) {
- boolean ignored = false;
- String modelName = getModelName(device);
- switch (modelName) {
- case "ZP80":
- modelName = "CONNECT";
- break;
- case "ZP100":
- modelName = "CONNECTAMP";
- break;
- case "One SL":
- modelName = "OneSL";
- break;
- case "Arc SL":
- modelName = "ArcSL";
- break;
- case "Roam SL":
- modelName = "RoamSL";
- break;
- case "Sub":
- // The Sonos Sub is ignored
- ignored = true;
- break;
- default:
- break;
- }
- if (!ignored) {
- ThingTypeUID thingUID = new ThingTypeUID(SonosBindingConstants.BINDING_ID, modelName);
- if (!SonosBindingConstants.SUPPORTED_KNOWN_THING_TYPES_UIDS.contains(thingUID)) {
+ String id = SonosXMLParser
+ .buildThingTypeIdFromModelName(device.getDetails().getModelDetails().getModelName());
+ if (!"Sub".equalsIgnoreCase(id)) {
+ ThingTypeUID thingTypeUID = new ThingTypeUID(SonosBindingConstants.BINDING_ID, id);
+ if (!SonosBindingConstants.SUPPORTED_KNOWN_THING_TYPES_UIDS.contains(thingTypeUID)) {
// Try with the model name all in uppercase
- thingUID = new ThingTypeUID(SonosBindingConstants.BINDING_ID, modelName.toUpperCase());
+ thingTypeUID = new ThingTypeUID(SonosBindingConstants.BINDING_ID, id.toUpperCase());
// In case a new "unknown" Sonos player is discovered a generic ThingTypeUID will be used
- if (!SonosBindingConstants.SUPPORTED_KNOWN_THING_TYPES_UIDS.contains(thingUID)) {
- thingUID = SonosBindingConstants.ZONEPLAYER_THING_TYPE_UID;
+ if (!SonosBindingConstants.SUPPORTED_KNOWN_THING_TYPES_UIDS.contains(thingTypeUID)) {
+ thingTypeUID = SonosBindingConstants.ZONEPLAYER_THING_TYPE_UID;
+ logger.warn(
+ "'{}' is not yet a supported model, thing type '{}' is considered as default; please open an issue",
+ device.getDetails().getModelDetails().getModelName(), thingTypeUID);
}
}
- logger.debug("Discovered a Sonos '{}' thing with UDN '{}'", thingUID,
+ logger.debug("Discovered a Sonos '{}' thing with UDN '{}'", thingTypeUID,
device.getIdentity().getUdn().getIdentifierString());
- return new ThingUID(thingUID, device.getIdentity().getUdn().getIdentifierString());
+ return new ThingUID(thingTypeUID, device.getIdentity().getUdn().getIdentifierString());
}
}
}
return null;
}
- private String getModelName(RemoteDevice device) {
- // For Ikea SYMFONISK models, the model name now starts with "SYMFONISK" with recent firmwares
- // We can no more use extractModelName as it deletes the first word ("Sonos" for all other devices)
- return device.getDetails().getModelDetails().getModelName().toUpperCase().contains("SYMFONISK") ? "SYMFONISK"
- : SonosXMLParser.extractModelName(device.getDetails().getModelDetails().getModelName());
- }
-
private @Nullable String getSonosRoomName(RemoteDevice device) {
return SonosXMLParser.getRoomName(device.getIdentity().getDescriptorURL().toString());
}
URL descriptor = service.getDescriptorURL(this);
if (descriptor != null) {
String sonosModelDescription = SonosXMLParser.parseModelDescription(descriptor);
- return sonosModelDescription == null ? null : SonosXMLParser.extractModelName(sonosModelDescription);
+ return sonosModelDescription == null ? null
+ : SonosXMLParser.buildThingTypeIdFromModelName(sonosModelDescription);
} else {
return null;
}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.sonos.internal;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.junit.jupiter.api.Test;
+
+/**
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class SonosXMLParserTest {
+
+ @Test
+ public void buildThingTypeIdFromModelWithoutSpace() {
+ assertEquals("Move", SonosXMLParser.buildThingTypeIdFromModelName("Sonos Move"));
+ }
+
+ @Test
+ public void buildThingTypeIdFromModelWithSpace() {
+ assertEquals("RoamSL", SonosXMLParser.buildThingTypeIdFromModelName("Sonos Roam SL"));
+ }
+
+ @Test
+ public void buildThingTypeIdFromModelWithColon() {
+ assertEquals("PLAY5", SonosXMLParser.buildThingTypeIdFromModelName("Sonos PLAY:5"));
+ }
+
+ @Test
+ public void buildThingTypeIdFromSymfoniskModel() {
+ assertEquals("SYMFONISK", SonosXMLParser.buildThingTypeIdFromModelName("SYMFONISK Table lamp"));
+ assertEquals("SYMFONISK", SonosXMLParser.buildThingTypeIdFromModelName("Symfonisk Table lamp"));
+ assertEquals("SYMFONISK", SonosXMLParser.buildThingTypeIdFromModelName("Sonos Symfonisk"));
+ }
+
+ @Test
+ public void buildThingTypeIdFromZP80Model() {
+ assertEquals("CONNECT", SonosXMLParser.buildThingTypeIdFromModelName("Sonos ZP80"));
+ }
+
+ @Test
+ public void buildThingTypeIdFromZP100Model() {
+ assertEquals("CONNECTAMP", SonosXMLParser.buildThingTypeIdFromModelName("Sonos ZP100"));
+ }
+
+ @Test
+ public void buildThingTypeIdFromModelWithAdditionalTextInParenthesis() {
+ assertEquals("OneSL", SonosXMLParser.buildThingTypeIdFromModelName("Sonos One SL (OpenHome)"));
+ }
+}