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

[mystrom] Add support for myStrom Bulb #9910

Merged
merged 6 commits into from
Feb 23, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions bundles/org.openhab.binding.mystrom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ This bundle adds the following thing types:
| Thing | ThingTypeID | Description |
| ------------------ | ----------- | -------------------------------------------------- |
| myStrom Smart Plug | mystromplug | A myStrom smart plug |
| myStrom Bulb | mystrombulb | A myStrom bulb |

According to the myStrom API documentation all request specific to the myStrom Bulb are also work on the LED strip.

## Discovery

Expand All @@ -24,13 +27,36 @@ The following parameters are valid for all thing types:
| hostname | string | yes | localhost | The IP address or hostname of the myStrom smart plug |
| refresh | integer | no | 10 | Poll interval in seconds. Increase this if you encounter connection errors |

## Properties

In addition to the configuration a myStrom thing has the following properties.
The properties are updated during initialize. Disabling/enabling the thing can be used to update the properties.
Fredo70 marked this conversation as resolved.
Show resolved Hide resolved

| Property-Name | Description |
| ------------- | --------------------------------------------------------------------- |
| version | Current firmware version |
| type | The type of the device (i.e. bulb = 102) |
| ssid | SSID of the currently connected network |
| ip | Current ip address |
| mask | Mask of the current network |
| gateway | Gateway of the current network |
| dns | DNS of the current network |
| static | Whether or not the ip address is static |
| connected | Whether or not the device is connected to the internet |
| mac | The mac address of the bridge in upper case letters without delimiter |

## Channels

| Channel ID | Item Type | Read only | Description |
| ---------------- | -------------------- | --------- | ------------------------------------------------------------- |
| switch | Switch | false | Turn the smart plug on or off |
| power | Number:Power | true | The currently delivered power |
| temperature | Number:Temperature | true | The temperature at the plug |
| Channel ID | Item Type | Read only | Description | Thing types supporting this channel |
| ---------------- | -------------------- | --------- | --------------------------------------------------------------------- |-------------------------------------|
| switch | Switch | false | Turn the device on or off | mystromplug, mystrombulb |
| power | Number:Power | true | The currently delivered power | mystromplug, mystrombulb |
| temperature | Number:Temperature | true | The temperature at the plug | mystromplug |
| color | Color | false | The color we set the bulb to (mode 'hsv') | mystrombulb |
| colorTemperature | Number | false | The color temperature of the bulb in mode 'mono' | mystrombulb |
| brightness | Dimmer | false | The brightness of the bulb in mode 'mono' | mystrombulb |
| ramp | Number:Time | false | Transition time from the light’s current state to the new state. [ms] | mystrombulb |
| mode | String | false | The color mode we want the Bulb to set to (rgb, hsv or mono) | mystrombulb |

## Full Example

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/**
* Copyright (c) 2010-2021 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.mystrom.internal;

import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_CONNECTED;
import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_DNS;
import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_GW;
import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_IP;
import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_LAST_REFRESH;
import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_MAC;
import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_MASK;
import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_SSID;
import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_STATIC;
import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_TYPE;
import static org.openhab.binding.mystrom.internal.MyStromBindingConstants.PROPERTY_VERSION;

import java.text.DateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;

import com.google.gson.Gson;

/**
* The {@link AbstractMyStromHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Frederic Chastagnol - Initial contribution
*/
@NonNullByDefault
public abstract class AbstractMyStromHandler extends BaseThingHandler {
protected static final String COMMUNICATION_ERROR = "Error while communicating to the myStrom plug: ";
protected static final String HTTP_REQUEST_URL_PREFIX = "http://";

protected final HttpClient httpClient;
protected String hostname = "";
protected String mac = "";

private @Nullable ScheduledFuture<?> pollingJob;
protected final Gson gson = new Gson();

public AbstractMyStromHandler(Thing thing, HttpClient httpClient) {
super(thing);
this.httpClient = httpClient;
}

@Override
public final void initialize() {
MyStromConfiguration config = getConfigAs(MyStromConfiguration.class);
this.hostname = HTTP_REQUEST_URL_PREFIX + config.hostname;

updateStatus(ThingStatus.UNKNOWN);
scheduler.schedule(this::initializeInternal, 0, TimeUnit.SECONDS);
}

@Override
public final void dispose() {
ScheduledFuture<?> pollingJob = this.pollingJob;
if (pollingJob != null) {
pollingJob.cancel(true);
this.pollingJob = null;
}
super.dispose();
}

private void updateProperties() throws MyStromException {
String json = sendHttpRequest(HttpMethod.GET, "/api/v1/info", null);
MyStromDeviceInfo deviceInfo = gson.fromJson(json, MyStromDeviceInfo.class);
if (deviceInfo == null) {
throw new MyStromException("Cannot retrieve device info from myStrom device " + getThing().getUID());
}
this.mac = deviceInfo.mac;
Map<String, String> properties = editProperties();
properties.put(PROPERTY_MAC, deviceInfo.mac);
properties.put(PROPERTY_VERSION, deviceInfo.version);
properties.put(PROPERTY_TYPE, Long.toString(deviceInfo.type));
properties.put(PROPERTY_SSID, deviceInfo.ssid);
properties.put(PROPERTY_IP, deviceInfo.ip);
properties.put(PROPERTY_MASK, deviceInfo.mask);
properties.put(PROPERTY_GW, deviceInfo.gw);
properties.put(PROPERTY_DNS, deviceInfo.dns);
properties.put(PROPERTY_STATIC, Boolean.toString(deviceInfo.staticState));
properties.put(PROPERTY_CONNECTED, Boolean.toString(deviceInfo.connected));
Calendar calendar = Calendar.getInstance();
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, Locale.getDefault());
properties.put(PROPERTY_LAST_REFRESH, formatter.format(calendar.getTime()));
updateProperties(properties);
}

/**
* Calls the API with the given http method, request path and actual data.
*
* @param method the http method to make the call with
* @param path The path of the API endpoint
* @param requestData the actual raw data to send in the request body, may be {@code null}
* @return String contents of the response for the GET request.
* @throws MyStromException Throws on communication error
*/
protected final String sendHttpRequest(HttpMethod method, String path, @Nullable String requestData)
throws MyStromException {
String url = hostname + path;
try {
Request request = httpClient.newRequest(url).timeout(10, TimeUnit.SECONDS).method(method);
if (requestData != null) {
request = request.content(new StringContentProvider(requestData)).header(HttpHeader.CONTENT_TYPE,
"application/x-www-form-urlencoded");
}
ContentResponse response = request.send();
if (response.getStatus() != HttpStatus.OK_200) {
throw new MyStromException("Error sending HTTP " + method + " request to " + url
+ ". Got response code: " + response.getStatus());
}
return response.getContentAsString();
} catch (InterruptedException | TimeoutException | ExecutionException e) {
throw new MyStromException(COMMUNICATION_ERROR + e.getMessage());
}
}

private void initializeInternal() {
try {
updateProperties();
updateStatus(ThingStatus.ONLINE);
MyStromConfiguration config = getConfigAs(MyStromConfiguration.class);
pollingJob = scheduler.scheduleWithFixedDelay(this::pollDevice, 0, config.refresh, TimeUnit.SECONDS);
} catch (MyStromException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
}
}

protected abstract void pollDevice();
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* used across the whole binding.
*
* @author Paul Frank - Initial contribution
* @author Frederic Chastagnol - Add constants for myStrom bulb support
*/
@NonNullByDefault
public class MyStromBindingConstants {
Expand All @@ -30,9 +31,36 @@ public class MyStromBindingConstants {

// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_PLUG = new ThingTypeUID(BINDING_ID, "mystromplug");
public static final ThingTypeUID THING_TYPE_BULB = new ThingTypeUID(BINDING_ID, "mystrombulb");

// List of all Channel ids
public static final String CHANNEL_SWITCH = "switch";
public static final String CHANNEL_POWER = "power";
public static final String CHANNEL_TEMPERATURE = "temperature";
public static final String CHANNEL_COLOR = "color";
public static final String CHANNEL_RAMP = "ramp";
public static final String CHANNEL_MODE = "mode";
public static final String CHANNEL_COLOR_TEMPERATURE = "colorTemperature";
public static final String CHANNEL_BRIGHTNESS = "brightness";

// Config
public static final String CONFIG_MAC = "mac";

// List of all Properties
public static final String PROPERTY_MAC = "mac";
public static final String PROPERTY_VERSION = "version";
public static final String PROPERTY_TYPE = "type";
public static final String PROPERTY_SSID = "ssid";
public static final String PROPERTY_IP = "ip";
public static final String PROPERTY_MASK = "mask";
public static final String PROPERTY_GW = "gw";
public static final String PROPERTY_DNS = "dns";
public static final String PROPERTY_STATIC = "static";
public static final String PROPERTY_CONNECTED = "connected";
public static final String PROPERTY_LAST_REFRESH = "lastRefresh";

// myStrom Bulb modes
public static final String RGB = "rgb";
public static final String HSV = "hsv";
public static final String MONO = "mono";
}
Loading