Skip to content

Commit

Permalink
Implement Shelly Pro 3 (#2572)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sn0w3y authored Jun 19, 2024
1 parent 514d5d4 commit 4cdae0c
Show file tree
Hide file tree
Showing 9 changed files with 541 additions and 19 deletions.
1 change: 1 addition & 0 deletions io.openems.edge.io.shelly/readme.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ Compatible with
- https://www.shelly.com/de/products/shop/shelly-plus-plug-s-1[Shelly Plus Plug S]
- https://www.shelly.com/de/products/shop/shelly-pro-3-em-120-a-1[Shelly Pro 3EM 3-Phase Meter]
- https://www.shelly.com/de/products/shop/shelly-plus-1-pm[Shelly Plus 1PM]
- https://www.shelly.com/de/products/shop/shelly-pro-3-1[Shelly Pro 3]

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
@@ -1,5 +1,6 @@
package io.openems.edge.io.shelly.common;

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

Expand All @@ -26,4 +27,29 @@ public static String generateDebugLog(Channel<Boolean> relayChannel, Channel<Int
return b.toString();
}

/**
* Generates a standard Debug-Log string for Shellys with multiple relays.
*
* @param digitalOutputChannels the Relay-Channels
* @return suitable for {@link OpenemsComponent#debugLog()}
*/
public static String generateDebugLog(BooleanWriteChannel[] digitalOutputChannels) {
// TODO share code with AbstractKmtronicRelay.debugLog()
var b = new StringBuilder();
var i = 1;
for (var channel : digitalOutputChannels) {
var valueOpt = channel.value().asOptional();
if (valueOpt.isPresent()) {
b.append(valueOpt.get() ? "x" : "-");
} else {
b.append("Unknown");
}
if (i < digitalOutputChannels.length) {
b.append("|");
}
i++;
}
return b.toString();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static io.openems.common.utils.JsonUtils.getAsBoolean;
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 java.util.Objects;

Expand Down Expand Up @@ -91,24 +92,7 @@ public BooleanWriteChannel[] digitalOutputChannels() {

@Override
public String debugLog() {
// TODO share code with AbstractKmtronicRelay.debugLog()
var b = new StringBuilder();
var i = 1;
for (var channel : this.digitalOutputChannels) {
String valueText;
var valueOpt = channel.value().asOptional();
if (valueOpt.isPresent()) {
valueText = valueOpt.get() ? "x" : "-";
} else {
valueText = "Unknown";
}
b.append(valueText);
if (i < this.digitalOutputChannels.length) {
b.append("|");
}
i++;
}
return b.toString();
return generateDebugLog(this.digitalOutputChannels);
}

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

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

@ObjectClassDefinition(//
name = "IO Shelly Pro 3", //
description = "Implements the Shelly Pro 3")
@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 = "IP-Address", description = "The IP address of the Shelly device.")
String ip();

String webconsole_configurationFactory_nameHint() default "IO Shelly Pro 3 [{id}]";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
package io.openems.edge.io.shelly.shellypro3;

import org.osgi.service.event.EventHandler;

import io.openems.common.channel.AccessMode;
import io.openems.common.channel.Level;
import io.openems.common.exceptions.OpenemsError.OpenemsNamedException;
import io.openems.common.types.OpenemsType;
import io.openems.edge.common.channel.BooleanDoc;
import io.openems.edge.common.channel.BooleanWriteChannel;
import io.openems.edge.common.channel.Doc;
import io.openems.edge.common.channel.StateChannel;
import io.openems.edge.common.channel.value.Value;
import io.openems.edge.common.component.OpenemsComponent;
import io.openems.edge.io.api.DigitalOutput;

public interface IoShellyPro3 extends DigitalOutput, OpenemsComponent, EventHandler {

public static enum ChannelId implements io.openems.edge.common.channel.ChannelId {
/**
* Holds writes to Relay Output 1 for debugging.
*
* <ul>
* <li>Interface: Shelly3Pro
* <li>Type: Boolean
* <li>Range: On/Off
* </ul>
*/
DEBUG_RELAY_1(Doc.of(OpenemsType.BOOLEAN)), //
/**
* Relay Output 1.
*
* <ul>
* <li>Interface: Shelly3Pro
* <li>Type: Boolean
* <li>Range: On/Off
* </ul>
*/
RELAY_1(new BooleanDoc() //
.accessMode(AccessMode.READ_WRITE) //
.onChannelSetNextWriteMirrorToDebugChannel(ChannelId.DEBUG_RELAY_1)),
/**
* Holds writes to Relay Output 2 for debugging.
*
* <ul>
* <li>Interface: Shelly3Pro
* <li>Type: Boolean
* <li>Range: On/Off
* </ul>
*/
DEBUG_RELAY_2(Doc.of(OpenemsType.BOOLEAN)), //
/**
* Relay Output 2.
*
* <ul>
* <li>Interface: Shelly3Pro
* <li>Type: Boolean
* <li>Range: On/Off
* </ul>
*/
RELAY_2(new BooleanDoc() //
.accessMode(AccessMode.READ_WRITE) //
.onChannelSetNextWriteMirrorToDebugChannel(ChannelId.DEBUG_RELAY_2)),
/**
* Holds writes to Relay Output 3 for debugging.
*
* <ul>
* <li>Interface: Shelly3Pro
* <li>Type: Boolean
* <li>Range: On/Off
* </ul>
*/
DEBUG_RELAY_3(Doc.of(OpenemsType.BOOLEAN)), //
/**
* Relay Output 3.
*
* <ul>
* <li>Interface: Shelly3Pro
* <li>Type: Boolean
* <li>Range: On/Off
* </ul>
*/
RELAY_3(new BooleanDoc() //
.accessMode(AccessMode.READ_WRITE) //
.onChannelSetNextWriteMirrorToDebugChannel(ChannelId.DEBUG_RELAY_3)),
/**
* Slave Communication Failed Fault.
*
* <ul>
* <li>Interface: Shelly3Pro
* <li>Type: State
* </ul>
*/
SLAVE_COMMUNICATION_FAILED(Doc.of(Level.FAULT)); //

private final Doc doc;

private ChannelId(Doc doc) {
this.doc = doc;
}

@Override
public Doc doc() {
return this.doc;
}
}

/**
* Gets the Channel for {@link ChannelId#RELAY_1}.
*
* @return the Channel
*/
public default BooleanWriteChannel getRelay1Channel() {
return this.channel(ChannelId.RELAY_1);
}

/**
* Gets the Relay Output 1. See {@link ChannelId#RELAY_1}.
*
* @return the Channel {@link Value}
*/
public default Value<Boolean> getRelay1() {
return this.getRelay1Channel().value();
}

/**
* Internal method to set the 'nextValue' on {@link ChannelId#RELAY_1} Channel.
*
* @param value the next value
*/
public default void _setRelay1(Boolean value) {
this.getRelay1Channel().setNextValue(value);
}

/**
* Sets the Relay Output 1. See {@link ChannelId#RELAY_1}.
*
* @param value the next write value
* @throws OpenemsNamedException on error
*/
public default void setRelay1(boolean value) throws OpenemsNamedException {
this.getRelay1Channel().setNextWriteValue(value);
}

/**
* Gets the Channel for {@link ChannelId#RELAY_2}.
*
* @return the Channel
*/
public default BooleanWriteChannel getRelay2Channel() {
return this.channel(ChannelId.RELAY_2);
}

/**
* Gets the Relay Output 2. See {@link ChannelId#RELAY_2}.
*
* @return the Channel {@link Value}
*/
public default Value<Boolean> getRelay2() {
return this.getRelay2Channel().value();
}

/**
* Internal method to set the 'nextValue' on {@link ChannelId#RELAY_2} Channel.
*
* @param value the next value
*/
public default void _setRelay2(Boolean value) {
this.getRelay2Channel().setNextValue(value);
}

/**
* Sets the Relay Output 2. See {@link ChannelId#RELAY_2}.
*
* @param value the next write value
* @throws OpenemsNamedException on error
*/
public default void setRelay2(boolean value) throws OpenemsNamedException {
this.getRelay2Channel().setNextWriteValue(value);
}

/**
* Gets the Channel for {@link ChannelId#RELAY_3}.
*
* @return the Channel
*/
public default BooleanWriteChannel getRelay3Channel() {
return this.channel(ChannelId.RELAY_3);
}

/**
* Gets the Relay Output 3. See {@link ChannelId#RELAY_3}.
*
* @return the Channel {@link Value}
*/
public default Value<Boolean> getRelay3() {
return this.getRelay3Channel().value();
}

/**
* Internal method to set the 'nextValue' on {@link ChannelId#RELAY_3} Channel.
*
* @param value the next value
*/
public default void _setRelay3(Boolean value) {
this.getRelay3Channel().setNextValue(value);
}

/**
* Sets the Relay Output 3. See {@link ChannelId#RELAY_3}.
*
* @param value the next write value
* @throws OpenemsNamedException on error
*/
public default void setRelay3(boolean value) throws OpenemsNamedException {
this.getRelay3Channel().setNextWriteValue(value);
}

/**
* Gets the Channel for {@link ChannelId#SLAVE_COMMUNICATION_FAILED}.
*
* @return the Channel
*/
public default StateChannel getSlaveCommunicationFailedChannel() {
return this.channel(ChannelId.SLAVE_COMMUNICATION_FAILED);
}

/**
* Gets the Slave Communication Failed State. See
* {@link ChannelId#SLAVE_COMMUNICATION_FAILED}.
*
* @return the Channel {@link Value}
*/
public default Value<Boolean> getSlaveCommunicationFailed() {
return this.getSlaveCommunicationFailedChannel().value();
}

/**
* Internal method to set the 'nextValue' on
* {@link ChannelId#SLAVE_COMMUNICATION_FAILED} Channel.
*
* @param value the next value
*/
public default void _setSlaveCommunicationFailed(boolean value) {
this.getSlaveCommunicationFailedChannel().setNextValue(value);
}
}
Loading

0 comments on commit 4cdae0c

Please sign in to comment.