Skip to content

Commit

Permalink
[mystrom] Add support for myStrom Bulb (openhab#9910)
Browse files Browse the repository at this point in the history
* Add support to myStrom Bulb

Add properties to myStrom devices and an action to refresh the properties.

Signed-off-by: Frederic Chastagnol <fchastagnol@fredoware.ch>

* Fixes according to review comments

Signed-off-by: Frederic Chastagnol <fchastagnol@fredoware.ch>

* Update bundles/org.openhab.binding.mystrom/README.md

Co-authored-by: J-N-K <J-N-K@users.noreply.github.com>

* Fixes according to review comments

Signed-off-by: Frederic Chastagnol <fchastagnol@fredoware.ch>

* Use system color temperature channel type

channel type system.color-temperature is used and values mapped from 1-18 to 0-100%

Signed-off-by: Frederic Chastagnol <fchastagnol@fredoware.ch>

* Better tracking of colour and brightness values

Format power state
Signed-off-by: Frederic Chastagnol <fchastagnol@fredoware.ch>

Co-authored-by: J-N-K <J-N-K@users.noreply.github.com>
  • Loading branch information
2 people authored and computergeek1507 committed Jul 13, 2021
1 parent d91dc34 commit 5539b7d
Show file tree
Hide file tree
Showing 8 changed files with 656 additions and 78 deletions.
37 changes: 32 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,37 @@ 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.

| 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 | Dimmer | false | The color temperature of the bulb in mode 'mono' (percentage) | 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

0 comments on commit 5539b7d

Please sign in to comment.