Skip to content
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

[mercedesme] Initial contribution #13044

Merged
merged 58 commits into from Aug 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
0faf8b7
initial commit
weymann May 28, 2022
b3e1d2b
mb callback function
weymann Jun 8, 2022
679dd94
rework auth
weymann Jun 15, 2022
89765bd
add callback server
weymann Jun 15, 2022
4a76022
bridge thing separation
weymann Jun 16, 2022
83229ef
first working authorization
weymann Jun 17, 2022
e5494d6
channel implementation
weymann Jun 18, 2022
e605006
store auth data in items
weymann Jun 19, 2022
79ebb8a
use StorageService to store token
weymann Jun 20, 2022
ef12817
first readme.md
weymann Jun 20, 2022
b61f84a
add radius channels
weymann Jun 20, 2022
374bcdc
add image support
weymann Jun 21, 2022
4570ff4
created seperate image cache
weymann Jun 22, 2022
9d2cea6
bugfixes for authorization
weymann Jun 22, 2022
012d992
bugfixes for authorization
weymann Jun 22, 2022
bc804d9
bugfixes for multi account usage
weymann Jun 22, 2022
4467f60
update readme
weymann Jun 22, 2022
b44088f
add StateOptionProvider
weymann Jun 23, 2022
dbbe2f7
add jpeg image support
weymann Jun 24, 2022
0a129c3
full example added
weymann Jun 25, 2022
fe04dd1
add last-update channel for each group
weymann Jun 25, 2022
951f4cb
bugfixes from community tests
weymann Jun 27, 2022
7051fd9
correct VehcileHandler status updates
weymann Jun 28, 2022
8768dd2
enhance debug information for API calls
weymann Jun 28, 2022
fe8a6b4
adjust loglevels
weymann Jun 28, 2022
3c1f5de
breaking change: token stored with key clientId
weymann Jun 28, 2022
b9eb76b
add translation
weymann Jun 29, 2022
36f15c3
add migration function to restore token from old keys
weymann Jun 30, 2022
8b90aee
optimize debugging in API calls
weymann Jun 30, 2022
4726424
add API call fallback solution
weymann Jun 30, 2022
0ea7424
cleanup unit tests
weymann Jun 30, 2022
4d9645a
resolve checkstyle warnings
weymann Jun 30, 2022
3e51cea
remove compiler warnings
weymann Jun 30, 2022
3069331
correct typos and links
weymann Jun 30, 2022
8e9b73e
bugfix port removal
weymann Jun 30, 2022
36f9cfe
add project in bom and pom
weymann Jun 30, 2022
08ffa9e
rebase
weymann Jun 30, 2022
4ac9f5f
bugfix unit test time with fixed time zone
weymann Jun 30, 2022
d33acc5
readme lights channel corrections
weymann Jul 2, 2022
db047db
optimize fallback calls
weymann Jul 2, 2022
2c56cb3
add charge/uncharged remain/open fuel channels
weymann Jul 2, 2022
11130ca
add tank/charge values in readme
weymann Jul 2, 2022
a78996e
minor logging & comment corrections
weymann Jul 7, 2022
326405a
fix translation file naming
weymann Jul 12, 2022
982ef41
remove unlinked pictures
weymann Jul 28, 2022
83048f7
review comment corrections part 1
weymann Jul 29, 2022
2ac3067
removed german translation
weymann Aug 2, 2022
8855ead
usage of binding specific Httplient
weymann Aug 4, 2022
c0aa6ae
add mercedesme translation provider
weymann Aug 4, 2022
7c55a82
rework status translations
weymann Aug 7, 2022
3d0e1dc
fix review comments
weymann Aug 16, 2022
27b9ae0
rework token and status handling
weymann Aug 16, 2022
26b063e
adapt link to binding URL after release
weymann Aug 16, 2022
324e3fe
take full control of listener update
weymann Aug 16, 2022
8d3ae05
remove permament token response logging
weymann Aug 18, 2022
c7e734c
fix json version
weymann Aug 18, 2022
b296695
review corrections
weymann Aug 22, 2022
9338fd2
fix review comments part 3
weymann Aug 23, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions bom/openhab-addons/pom.xml
Expand Up @@ -901,6 +901,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
@@ -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.

Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
@@ -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>
@@ -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>
@@ -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 = ":";
weymann marked this conversation as resolved.
Show resolved Hide resolved
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);
}
@@ -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;
}
}
@@ -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());
}
}
}
@@ -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;
}
}