Skip to content

Commit

Permalink
[mercedesme] Initial contribution (openhab#13044)
Browse files Browse the repository at this point in the history
Signed-off-by: Bernd Weymann <[email protected]>
  • Loading branch information
weymann authored and psmedley committed Feb 23, 2023
1 parent 1cad75e commit f7cf8f6
Show file tree
Hide file tree
Showing 65 changed files with 4,074 additions and 0 deletions.
5 changes: 5 additions & 0 deletions bom/openhab-addons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,11 @@
<artifactId>org.openhab.binding.melcloud</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.mercedesme</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.meteoalerte</artifactId>
Expand Down
13 changes: 13 additions & 0 deletions bundles/org.openhab.binding.mercedesme/NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.

* Project home: https://www.openhab.org

== Declared Project Licenses

This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.

== Source Code

https://github.com/openhab/openhab-addons
503 changes: 503 additions & 0 deletions bundles/org.openhab.binding.mercedesme/README.md

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions bundles/org.openhab.binding.mercedesme/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.4.0-SNAPSHOT</version>
</parent>

<dependencies>
<!-- version needs to match with other projects like org.openhab.io.openhabcloud.pom.xml -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180813</version>
<scope>compile</scope>
</dependency>
</dependencies>

<artifactId>org.openhab.binding.mercedesme</artifactId>

<name>openHAB Add-ons :: Bundles :: MercedesMe Binding</name>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.mercedesme-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>

<feature name="openhab-binding-mercedesme" description="MercedesMe Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.mercedesme/${project.version}</bundle>
</feature>
</features>
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
* 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.mercedesme.internal;

import javax.measure.Unit;
import javax.measure.quantity.Length;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.unit.MetricPrefix;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.thing.ThingTypeUID;

/**
* The {@link Constants} class defines common constants, which are
* used across the whole binding.
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
public class Constants {
public static final String BINDING_ID = "mercedesme";

public static final String COMBUSTION = "combustion";
public static final String HYBRID = "hybrid";
public static final String BEV = "bev";

// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_ACCOUNT = new ThingTypeUID(BINDING_ID, "account");
public static final ThingTypeUID THING_TYPE_COMB = new ThingTypeUID(BINDING_ID, COMBUSTION);
public static final ThingTypeUID THING_TYPE_HYBRID = new ThingTypeUID(BINDING_ID, HYBRID);
public static final ThingTypeUID THING_TYPE_BEV = new ThingTypeUID(BINDING_ID, BEV);

public static final String GROUP_RANGE = "range";
public static final String GROUP_DOORS = "doors";
public static final String GROUP_WINDOWS = "windows";
public static final String GROUP_LOCK = "lock";
public static final String GROUP_LIGHTS = "lights";
public static final String GROUP_LOCATION = "location";
public static final String GROUP_IMAGE = "image";

public static final String MB_AUTH_URL = "https://id.mercedes-benz.com/as/authorization.oauth2";
public static final String MB_TOKEN_URL = "https://id.mercedes-benz.com/as/token.oauth2";
public static final String CALLBACK_ENDPOINT = "/mb-callback";
public static final String OAUTH_CLIENT_NAME = "#byocar";

// https://developer.mercedes-benz.com/products/electric_vehicle_status/docs
public static final String SCOPE_EV = "mb:vehicle:mbdata:evstatus";
// https://developer.mercedes-benz.com/products/fuel_status/docs
public static final String SCOPE_FUEL = "mb:vehicle:mbdata:fuelstatus";
// https://developer.mercedes-benz.com/products/pay_as_you_drive_insurance/docs
public static final String SCOPE_ODO = "mb:vehicle:mbdata:payasyoudrive";
// https://developer.mercedes-benz.com/products/vehicle_lock_status/docs
public static final String SCOPE_LOCK = "mb:vehicle:mbdata:vehiclelock";
// https://developer.mercedes-benz.com/products/vehicle_status/docs
public static final String SCOPE_STATUS = "mb:vehicle:mbdata:vehiclestatus";
public static final String SCOPE_OFFLINE = "offline_access";

public static final String BASE_URL = "https://api.mercedes-benz.com/vehicledata/v2";
public static final String ODO_URL = BASE_URL + "/vehicles/%s/containers/payasyoudrive";
public static final String STATUS_URL = BASE_URL + "/vehicles/%s/containers/vehiclestatus";
public static final String LOCK_URL = BASE_URL + "/vehicles/%s/containers/vehiclelockstatus";
public static final String FUEL_URL = BASE_URL + "/vehicles/%s/containers/fuelstatus";
public static final String EV_URL = BASE_URL + "/vehicles/%s/containers/electricvehicle";

// https://developer.mercedes-benz.com/content-page/api_migration_guide
public static final String IMAGE_BASE_URL = "https://api.mercedes-benz.com/vehicle_images/v2";
public static final String IMAGE_EXTERIOR_RESOURCE_URL = IMAGE_BASE_URL + "/vehicles/%s";

public static final String STATUS_TEXT_PREFIX = "@text/mercedesme.";
public static final String STATUS_AUTH_NEEDED = ".status.authorization-needed";
public static final String STATUS_IP_MISSING = ".status.ip-missing";
public static final String STATUS_PORT_MISSING = ".status.port-missing";
public static final String STATUS_CLIENT_ID_MISSING = ".status.client-id-missing";
public static final String STATUS_CLIENT_SECRET_MISSING = ".status.client-secret-missing";
public static final String STATUS_SERVER_RESTART = ".status.server-restart";
public static final String STATUS_BRIDGE_MISSING = ".status.bridge-missing";
public static final String STATUS_BRIDGE_ATHORIZATION = ".status.bridge-authoriziation";

public static final String SPACE = " ";
public static final String EMPTY = "";
public static final String COLON = ":";
public static final String NOT_SET = "not set";

public static final String CODE = "code";
public static final String MIME_PREFIX = "image/";

public static final Unit<Length> KILOMETRE_UNIT = MetricPrefix.KILO(SIUnits.METRE);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* 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.mercedesme.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.thing.binding.BaseDynamicCommandDescriptionProvider;
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
import org.openhab.core.thing.type.DynamicCommandDescriptionProvider;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

/**
* Dynamic provider of command options while leaving other state description fields as original.
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
@Component(service = { DynamicCommandDescriptionProvider.class, MercedesMeCommandOptionProvider.class })
public class MercedesMeCommandOptionProvider extends BaseDynamicCommandDescriptionProvider {
@Activate
public MercedesMeCommandOptionProvider(final @Reference EventPublisher eventPublisher, //
final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry, //
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
this.eventPublisher = eventPublisher;
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
* 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.mercedesme.internal;

import static org.openhab.binding.mercedesme.internal.Constants.*;

import java.util.Set;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.WWWAuthenticationProtocolHandler;
import org.openhab.binding.mercedesme.internal.handler.AccountHandler;
import org.openhab.binding.mercedesme.internal.handler.VehicleHandler;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.storage.StorageService;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The {@link MercedesMeHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.mercedesme", service = ThingHandlerFactory.class)
public class MercedesMeHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_BEV, THING_TYPE_COMB,
THING_TYPE_HYBRID, THING_TYPE_ACCOUNT);

private final Logger logger = LoggerFactory.getLogger(MercedesMeHandlerFactory.class);
private final OAuthFactory oAuthFactory;
private final HttpClient httpClient;
private final MercedesMeCommandOptionProvider mmcop;
private final MercedesMeStateOptionProvider mmsop;
private final StorageService storageService;
private final TimeZoneProvider timeZoneProvider;

@Activate
public MercedesMeHandlerFactory(@Reference OAuthFactory oAuthFactory, @Reference HttpClientFactory hcf,
@Reference StorageService storageService, final @Reference MercedesMeCommandOptionProvider cop,
final @Reference MercedesMeStateOptionProvider sop, final @Reference TimeZoneProvider tzp) {
this.oAuthFactory = oAuthFactory;
this.storageService = storageService;
mmcop = cop;
mmsop = sop;
timeZoneProvider = tzp;
httpClient = hcf.createHttpClient(Constants.BINDING_ID);
// https://github.com/jetty-project/jetty-reactive-httpclient/issues/33
httpClient.getProtocolHandlers().remove(WWWAuthenticationProtocolHandler.NAME);
try {
httpClient.start();
} catch (Exception e) {
logger.warn("HTTP client not started: {} - no web access possible!", e.getLocalizedMessage());
}
}

@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}

@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_ACCOUNT.equals(thingTypeUID)) {
return new AccountHandler((Bridge) thing, httpClient, oAuthFactory);
}
return new VehicleHandler(thing, httpClient, thingTypeUID.getId(), storageService, mmcop, mmsop,
timeZoneProvider);
}

@Override
protected void deactivate(ComponentContext componentContext) {
super.deactivate(componentContext);
try {
httpClient.stop();
} catch (Exception e) {
logger.debug("HTTP client not stopped: {}", e.getLocalizedMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* 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.mercedesme.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

/**
* Dynamic provider of state options while leaving other state description fields as original.
*
* @author Bernd Weymann - Initial contribution
*/
@NonNullByDefault
@Component(service = { DynamicStateDescriptionProvider.class, MercedesMeStateOptionProvider.class })
public class MercedesMeStateOptionProvider extends BaseDynamicStateDescriptionProvider {
@Activate
public MercedesMeStateOptionProvider(final @Reference EventPublisher eventPublisher, //
final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry, //
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
this.eventPublisher = eventPublisher;
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
}
}
Loading

0 comments on commit f7cf8f6

Please sign in to comment.