Skip to content

Commit

Permalink
Implement new Shelly Plus Plug S (#2529)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sn0w3y authored Apr 29, 2024
1 parent 3f42033 commit 021e674
Show file tree
Hide file tree
Showing 15 changed files with 754 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -1,27 +1,52 @@
package io.openems.edge.bridge.http.dummy;

import static java.util.concurrent.CompletableFuture.completedFuture;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

import io.openems.edge.bridge.http.api.BridgeHttp;

public class DummyBridgeHttp implements BridgeHttp {

public final List<CycleEndpoint> cycleEndpoints = new ArrayList<>();
public final List<TimeEndpoint> timeEndpoints = new ArrayList<>();

private String nextRequestResult = null;

@Override
public void subscribeCycle(CycleEndpoint endpoint) {
// TODO Auto-generated method stub

this.cycleEndpoints.add(endpoint);
}

@Override
public void subscribeTime(TimeEndpoint endpoint) {
// TODO Auto-generated method stub

this.timeEndpoints.add(endpoint);
}

@Override
public CompletableFuture<String> request(Endpoint endpoint) {
// TODO Auto-generated method stub
return null;
return completedFuture(this.nextRequestResult);
}

/**
* Mocks a result for all {@link CycleEndpoint}s.
*
* @param result the mocked read result
*/
public void mockCycleResult(String result) {
this.cycleEndpoints.forEach(//
e -> e.result().accept(result));
}

/**
* Mocks a result for simple request {@link Endpoint}.
*
* @param nextRequestResult the mocked read result
*/
public void mockRequestResult(String nextRequestResult) {
this.nextRequestResult = nextRequestResult;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,25 @@

public class DummyBridgeHttpFactory extends BridgeHttpFactory {

public final DummyBridgeHttp bridge = new DummyBridgeHttp();

public DummyBridgeHttpFactory() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
super();
ReflectionUtils.setAttribute(BridgeHttpFactory.class, this, "csoBridgeHttp", new DummyBridgeHttpCso());
ReflectionUtils.setAttribute(BridgeHttpFactory.class, this, "csoBridgeHttp",
new DummyBridgeHttpCso(this.bridge));
}

private static class DummyBridgeHttpCso implements ComponentServiceObjects<BridgeHttp> {

private final DummyBridgeHttp bridge;

public DummyBridgeHttpCso(DummyBridgeHttp bridge) {
this.bridge = bridge;
}

@Override
public BridgeHttp getService() {
return new DummyBridgeHttp();
return this.bridge;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,17 @@ public SELF activate(AbstractComponentConfig config) throws Exception {
return this.self();
}

/**
* Calls the 'deactivate()' method of the 'system-under-test'.
*
* @return itself, to use as a builder
* @throws Exception on error
*/
public SELF deactivate() throws Exception {
this.callDeactivate();
return this.self();
}

private int getConfigChangeCount() throws IOException, InvalidSyntaxException {
var result = 0;
for (Object object : this.references) {
Expand Down
9 changes: 4 additions & 5 deletions io.openems.edge.io.shelly/readme.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
This bundle implements Shelly WiFi Relay Switches.

Compatible with
- https://shelly.cloud/products/shelly-25-smart-home-automation-relay/[Shelly 2.5]
- https://shelly.cloud/products/shelly-plug-s-smart-home-automation-device/[Shelly Plug S]

Implemented Natures
- DigitalOutput
- https://www.shelly.com/de/products/shop/1xs25[Shelly 2.5]
- https://www.shelly.com/en/products/shop/shelly-3-em[Shelly 3EM]
- Shelly Plug S
- https://www.shelly.com/de/products/shop/shelly-plus-plug-s-1[Shelly Plus Plug S]

https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.io.shelly[Source Code icon:github[]]
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.openems.edge.io.shelly.common;

import io.openems.edge.common.channel.Channel;
import io.openems.edge.common.component.OpenemsComponent;

public class Utils {

private Utils() {
}

/**
* Generates a standard Debug-Log string for Shellys with one relay and power
* meter.
*
* @param relayChannel the Relay-Channel
* @param activePowerChannel the ActivePower-Channel
* @return suitable for {@link OpenemsComponent#debugLog()}
*/
public static String generateDebugLog(Channel<Boolean> relayChannel, Channel<Integer> activePowerChannel) {
var b = new StringBuilder();
relayChannel.value().asOptional().ifPresentOrElse(//
v -> b.append(v ? "On" : "Off"), //
() -> b.append("Unknown"));
b.append("|");
b.append(activePowerChannel.value().asString());
return b.toString();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.common.event.EdgeEventConstants;
import io.openems.edge.io.api.DigitalOutput;
import io.openems.edge.io.shelly.common.ShellyApi;

@Designate(ocd = Config.class, factory = true)
@Component(//
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.openems.edge.io.shelly.common;
package io.openems.edge.io.shelly.shelly25;

import java.io.BufferedReader;
import java.io.IOException;
Expand All @@ -13,6 +13,7 @@
import io.openems.common.exceptions.OpenemsException;
import io.openems.common.utils.JsonUtils;

// TODO replace with HttpBridge
/**
* Implements the local Shelly REST Api.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static io.openems.common.utils.JsonUtils.getAsFloat;
import static io.openems.common.utils.JsonUtils.getAsJsonArray;
import static io.openems.common.utils.JsonUtils.getAsJsonObject;
import static io.openems.edge.io.shelly.common.Utils.generateDebugLog;
import static java.lang.Math.round;

import java.util.Objects;
Expand Down Expand Up @@ -109,17 +110,7 @@ public BooleanWriteChannel[] digitalOutputChannels() {

@Override
public String debugLog() {
var b = new StringBuilder();
var valueOpt = this.getRelayChannel().value().asOptional();
b.append(valueOpt.isPresent() //
? (valueOpt.get() //
? "ON" //
: "OFF") //
: "Unknown");
b.append("|");
b.append(this.getActivePowerChannel().value().asString());

return b.toString();
return generateDebugLog(this.getRelayChannel(), this.getActivePowerChannel());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.openems.edge.io.shelly.shellyplug;

import static io.openems.edge.io.shelly.common.Utils.generateDebugLog;

import java.util.Objects;

import org.osgi.service.component.ComponentContext;
Expand Down Expand Up @@ -99,16 +101,7 @@ public BooleanWriteChannel[] digitalOutputChannels() {

@Override
public String debugLog() {
var b = new StringBuilder();
var valueOpt = this.getRelayChannel().value().asOptional();
if (valueOpt.isPresent()) {
b.append(valueOpt.get() ? "On" : "Off");
} else {
b.append("Unknown");
}
b.append("|");
b.append(this.getActivePowerChannel().value().asString());
return b.toString();
return generateDebugLog(this.getRelayChannel(), this.getActivePowerChannel());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.openems.edge.io.shelly.shellyplusplugs;

import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;

import io.openems.edge.meter.api.MeterType;
import io.openems.edge.meter.api.SinglePhase;

@ObjectClassDefinition(//
name = "IO Shelly Plus Plug S", //
description = "Implements the Shelly Plus Plug S")
@interface Config {

@AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component")
String id() default "io0";

@AttributeDefinition(name = "Alias", description = "Human-readable name of this Component; defaults to Component-ID")
String alias() default "";

@AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?")
boolean enabled() default true;

@AttributeDefinition(name = "Phase", description = "Which Phase is this Shelly Plug connected to?")
SinglePhase phase() default SinglePhase.L1;

@AttributeDefinition(name = "IP-Address", description = "The IP address of the Shelly device.")
String ip();

@AttributeDefinition(name = "Meter-Type", description = "What is measured by this Meter?")
MeterType type() default MeterType.CONSUMPTION_METERED;

String webconsole_configurationFactory_nameHint() default "IO Shelly Plus Plug S [{id}]";
}
Loading

0 comments on commit 021e674

Please sign in to comment.