Skip to content

Commit

Permalink
Merge branch 'openhab:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
HolgerHees authored Mar 17, 2024
2 parents 69cd8b0 + 3039f1f commit 5cb2d06
Show file tree
Hide file tree
Showing 79 changed files with 1,539 additions and 330 deletions.
2 changes: 2 additions & 0 deletions bundles/org.openhab.binding.dsmr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ The following channels are supported:
| `emeter_active_import_power` | Number:Power | Aggregate active import power (W) | Y | - | - | - | - | - | - | - | - | - | - |
| `emeter_actual_delivery` | Number:Power | Current power delivery (kW) | - | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
| `emeter_actual_production` | Number:Power | Current power production (kW) | - | - | - | Y | Y | Y | Y | Y | Y | Y | Y |
| `emeter_actual_demand` | Number:Power | Current power delivery demand (kW) | - | - | - | Y | Y | Y | Y | Y | Y | Y | Y |
| `emeter_maximum_demand_current_month` | Number:Power | Maximum power delivery demand current month (kW) | - | - | - | Y | Y | Y | Y | Y | Y | Y | Y |
| `emeter_actual_reactive_delivery` | Number | Actual Reactive Power Delivery (kvar) | - | - | - | - | - | - | - | - | Y | - | Y |
| `emeter_actual_reactive_production` | Number | Actual Reactive Power Production (kvar) | - | - | - | - | - | - | - | - | Y | - | Y |
| `emeter_active_threshold_smax` | Number | Active threshold (SMAX) (kVA) | - | - | - | - | - | - | - | - | Y | - | - |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
*
* @author M. Volaart - Initial contribution
* @author Hilbrand Bouwkamp - Cosem subclasses made into factory classes and introduced quantity type
* @author Lennert Coopman - Added capacity tariff channels for Belgium: actual and current month
*/
@NonNullByDefault
public enum CosemObjectType {
Expand Down Expand Up @@ -69,6 +70,8 @@ public enum CosemObjectType {
EMETER_TARIFF_INDICATOR(new OBISIdentifier(0, 96, 14, 0), CosemString.INSTANCE),
EMETER_ACTIVE_IMPORT_POWER(new OBISIdentifier(1, 15, 7, 0), CosemQuantity.WATT),
EMETER_ACTUAL_DELIVERY(new OBISIdentifier(1, 1, 7, 0), CosemQuantity.KILO_WATT),
EMETER_ACTUAL_DEMAND(new OBISIdentifier(1, 1, 4, 0), CosemQuantity.KILO_WATT),
EMETER_MAXIMUM_DEMAND_CURRENT_MONTH(new OBISIdentifier(1, 1, 6, 0), CosemDate.INSTANCE, CosemQuantity.KILO_WATT),
EMETER_ACTUAL_PRODUCTION(new OBISIdentifier(1, 2, 7, 0), CosemQuantity.KILO_WATT),
EMETER_TRESHOLD_A_V2_1(new OBISIdentifier(1, 17, 0, 0), CosemQuantity.AMPERE),
EMETER_TRESHOLD_A(new OBISIdentifier(0, 17, 0, 0, true), CosemQuantity.AMPERE),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
* Supported meters
*
* @author M. Volaart - Initial contribution
* @author Lennert Coopman - Added capacity tariff channels for Belgium: actual and current month
*/
@NonNullByDefault
public enum DSMRMeterType {
Expand Down Expand Up @@ -341,7 +342,8 @@ public enum DSMRMeterType {
CosemObjectType.EMETER_INSTANT_POWER_PRODUCTION_L2, CosemObjectType.EMETER_INSTANT_POWER_PRODUCTION_L3,
CosemObjectType.EMETER_INSTANT_CURRENT_L1, CosemObjectType.EMETER_INSTANT_CURRENT_L2,
CosemObjectType.EMETER_INSTANT_CURRENT_L3, CosemObjectType.EMETER_INSTANT_VOLTAGE_L1,
CosemObjectType.EMETER_INSTANT_VOLTAGE_L2, CosemObjectType.EMETER_INSTANT_VOLTAGE_L3
CosemObjectType.EMETER_INSTANT_VOLTAGE_L2, CosemObjectType.EMETER_INSTANT_VOLTAGE_L3,
CosemObjectType.EMETER_ACTUAL_DEMAND, CosemObjectType.EMETER_MAXIMUM_DEMAND_CURRENT_MONTH
}),

/** Belgium Smart Gas Meter for the e-MUCS specification */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ channel-type.dsmr.activeImportPowerType.label = Aggregate Active Import Power
channel-type.dsmr.activeImportPowerType.description = The aggregate active import power.
channel-type.dsmr.activeThresholdSmax.label = Active Threshold
channel-type.dsmr.activeThresholdSmax.description = Active threshold (SMAX).
channel-type.dsmr.actualDeliveryBelgiumType.label = Actual Power Delivery
channel-type.dsmr.actualDeliveryBelgiumType.description = The current power delivery.
channel-type.dsmr.actualDeliveryType.label = Actual Power Delivery
channel-type.dsmr.actualDeliveryType.description = The current power delivery.
channel-type.dsmr.actualFuseThresholdAType.label = Actual Fuse Threshold
Expand Down Expand Up @@ -321,7 +323,6 @@ addon.dsmr.error.configuration.invalidsmartykey = The given Smarty decyption key
addon.dsmr.error.configuration.invalid.decryptionKey = The given Smarty decyption key is invalid. It contains an illegal character: {0}
addon.dsmr.error.configuration.invalid.additionalKey = The given Smarty additional key is invalid. It contains an illegal character: {0}
addon.dsmr.error.thing.nodata = Not receiving data from meter.

addon.dsmr.error.status.invalid_decryption_key = Failed to decrypt P1 telegram due to invalid encryption key
addon.dsmr.error.status.port_dont_exists = Serial port does not exist.
addon.dsmr.error.status.port_in_use = Serial port is already in use.
Expand All @@ -332,4 +333,3 @@ addon.dsmr.error.status.serial_data_read_error = Reading data from the serial po
addon.dsmr.error.status.telegram_crc_error = CRC checksum failed for received P1 telegram.
addon.dsmr.error.status.telegram_data_corruption = Received P1 telegram is corrupted. Possible bad/wrong P1 data cable?
addon.dsmr.error.status.telegram_no_data = Received telegram data, but after parsing no data is present. Possible all data corrupted.

Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,12 @@
<description>The current power delivery.</description>
<state pattern="%.3f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="actualDeliveryBelgiumType">
<item-type>Number:Power</item-type>
<label>Actual Power Delivery</label>
<description>The current power delivery.</description>
<state pattern="%.3f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="actualProductionType" advanced="true">
<item-type>Number:Power</item-type>
<label>Actual Power Production</label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
<channel id="emeter_tariff_indicator" typeId="tariffIndicatorType"/>
<channel id="emeter_actual_delivery" typeId="actualDeliveryType"/>
<channel id="emeter_actual_production" typeId="actualProductionType"/>
<channel id="emeter_actual_demand" typeId="actualDeliveryBelgiumType"/>
<channel id="emeter_maximum_demand_current_month" typeId="actualDeliveryBelgiumType">
<label>Power Delivery Current Month</label>
</channel>
<channel id="emeter_switch_position" typeId="switchPositionType"/>
<channel id="emeter_fuse_threshold_a" typeId="actualFuseThresholdAType"/>
<channel id="emeter_treshold_kw" typeId="actualTresholdkWType"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public EcovacsApiException(String reason) {
this(reason, false);
}

public EcovacsApiException(String reason, Throwable cause) {
super(reason, cause);
isAuthFailure = false;
}

public EcovacsApiException(String reason, boolean isAuthFailure) {
super(reason);
this.isAuthFailure = isAuthFailure;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,8 @@ public Optional<Integer> convertResponse(AbstractPortalIotCommandResponse respon
Gson gson) throws DataParsingException {
if (response instanceof PortalIotCommandJsonResponse jsonResponse) {
ErrorReport resp = jsonResponse.getResponsePayloadAs(gson, ErrorReport.class);
if (resp.errorCodes.isEmpty()) {
return Optional.empty();
}
return Optional.of(resp.errorCodes.get(0));
int responseCode = resp.errorCodes.isEmpty() ? 0 : resp.errorCodes.get(0);
return Optional.of(responseCode);
} else {
String payload = ((PortalIotCommandXmlResponse) response).getResponsePayloadXml();
return DeviceInfo.parseErrorInfo(payload);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@
*/
@NonNullByDefault
public class SpotAreaCleaningCommand extends AbstractAreaCleaningCommand {
public SpotAreaCleaningCommand(List<String> roomIds, int cleanPasses) {
super("spotArea", String.join(",", roomIds), cleanPasses);
public SpotAreaCleaningCommand(List<String> roomIds, int cleanPasses, boolean usesFreeClean) {
super(usesFreeClean ? "freeClean" : "spotArea", prepareRoomIds(roomIds, usesFreeClean), cleanPasses);
}

private static String prepareRoomIds(List<String> roomIds, boolean usesFreeClean) {
String prefix = usesFreeClean ? "1," : "";
String separator = usesFreeClean ? ";" : ",";
return prefix + String.join(separator, roomIds);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -345,13 +345,18 @@ private <T> T handleResponseWrapper(@Nullable ResponseWrapper<T> response) throw
}

private <T> T handleResponse(ContentResponse response, Class<T> clazz) throws EcovacsApiException {
@Nullable
T respObject = gson.fromJson(response.getContentAsString(), clazz);
if (respObject == null) {
// should not happen in practice
throw new EcovacsApiException("No response received");
try {
@Nullable
T respObject = gson.fromJson(response.getContentAsString(), clazz);
if (respObject == null) {
// should not happen in practice
throw new EcovacsApiException("No response received");
}
return respObject;
} catch (JsonSyntaxException e) {
throw new EcovacsApiException("Failed to parse response '" + response.getContentAsString()
+ "' as data class " + clazz.getSimpleName(), e);
}
return respObject;
}

private Request createAuthRequest(String url, String clientKey, String clientSecret,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,12 @@ public void handleMessage(String eventName, String payload) throws DataParsingEx
}
case "error": {
ErrorReport report = payloadAs(response, ErrorReport.class);
for (Integer code : report.errorCodes) {
listener.onErrorReported(device, code);
if (report.errorCodes.isEmpty()) {
listener.onErrorReported(device, 0);
} else {
for (Integer code : report.errorCodes) {
listener.onErrorReported(device, code);
}
}
}
case "stats": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public enum CleanMode {
EDGE,
@SerializedName("spot")
SPOT,
@SerializedName(value = "SpotArea", alternate = { "spotArea" })
@SerializedName(value = "SpotArea", alternate = { "spotArea", "freeClean" })
SPOT_AREA,
@SerializedName(value = "CustomArea", alternate = { "customArea" })
CUSTOM_AREA,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public enum DeviceCapability {
VOICE_REPORTING,
@SerializedName("spot_area_cleaning")
SPOT_AREA_CLEANING,
@SerializedName("free_clean_command")
FREE_CLEAN_FOR_SPOT_AREA,
@SerializedName("custom_area_cleaning")
CUSTOM_AREA_CLEANING,
@SerializedName("single_room_cleaning")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,8 @@ private State stringToState(@Nullable String value) {
}
}
if (!roomIds.isEmpty()) {
return new SpotAreaCleaningCommand(roomIds, passes);
return new SpotAreaCleaningCommand(roomIds, passes,
device.hasCapability(DeviceCapability.FREE_CLEAN_FOR_SPOT_AREA));
}
} else {
logger.info("{}: spotArea command needs to have the form spotArea:<room1>[;<room2>][;<...roomX>][:x2]",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,5 +557,41 @@
"modelName": "DEEBOT U2 SE",
"deviceClass": "zjna8m",
"deviceClassLink": "ipzjy0"
},

{
"modelName": "DEEBOT X2",
"deviceClass": "e6ofmn",
"protoVersion": "json_v2",
"usesMqtt": true,
"capabilities": [
"mopping_system",
"main_brush",
"spot_area_cleaning",
"free_clean_command",
"custom_area_cleaning",
"clean_speed_control",
"voice_reporting",
"read_network_info",
"unit_care_lifespan",
"true_detect_3d",
"mapping",
"auto_empty_station"
]
},
{
"modelName": "DEEBOT X2 OMNI",
"deviceClass": "lf3bn4",
"deviceClassLink": "e6ofmn"
},
{
"modelName": "DEEBOT X2 COMBO",
"deviceClass": "e6rcnf",
"deviceClassLink": "e6ofmn"
},
{
"modelName": "DEEBOT X2 PRO OMNI",
"deviceClass": "ip3mmy",
"deviceClassLink": "e6ofmn"
}
]
35 changes: 24 additions & 11 deletions bundles/org.openhab.binding.energidataservice/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,24 @@ It will not impact channels, see [Electricity Tax](#electricity-tax) for further

### Channel Group `electricity`

| Channel | Type | Description | Advanced |
|--------------------------|--------------------|--------------------------------------------------------------------------------|----------|
| spot-price | Number:EnergyPrice | Spot price in DKK or EUR per kWh | no |
| grid-tariff | Number:EnergyPrice | Grid tariff in DKK per kWh. Only available when `gridCompanyGLN` is configured | no |
| system-tariff | Number:EnergyPrice | System tariff in DKK per kWh | no |
| transmission-grid-tariff | Number:EnergyPrice | Transmission grid tariff in DKK per kWh | no |
| electricity-tax | Number:EnergyPrice | Electricity tax in DKK per kWh | no |
| reduced-electricity-tax | Number:EnergyPrice | Reduced electricity tax in DKK per kWh. For electric heating customers only | no |
| Channel | Type | Description |
|--------------------------|--------------------------|----------------------------------------------------------------------------------------|
| spot-price | Number:EnergyPrice | Spot price in DKK or EUR per kWh |
| grid-tariff | Number:EnergyPrice | Grid tariff in DKK per kWh. Only available when `gridCompanyGLN` is configured |
| system-tariff | Number:EnergyPrice | System tariff in DKK per kWh |
| transmission-grid-tariff | Number:EnergyPrice | Transmission grid tariff in DKK per kWh |
| electricity-tax | Number:EnergyPrice | Electricity tax in DKK per kWh |
| reduced-electricity-tax | Number:EnergyPrice | Reduced electricity tax in DKK per kWh. For electric heating customers only |
| co2-emission-prognosis | Number:EmissionIntensity | Estimated prognosis for CO₂ emission following the day-ahead market in g/kWh |
| co2-emission-realtime | Number:EmissionIntensity | Near up-to-date history for CO₂ emission from electricity consumed in Denmark in g/kWh |

_Please note:_ There is no channel providing the total price.
Instead, create a group item with `SUM` as aggregate function and add the individual price items as children.
This has the following advantages:

- Full customization possible: Freely choose the channels which should be included in the total.
- An additional item containing the kWh fee from your electricity supplier can be added also.
- Spot price can be configured in EUR while tariffs are in DKK.
- Full customization possible: Freely choose the channels which should be included in the total (even between different bindings).
- Spot price can be configured in EUR while tariffs are in DKK (and currency conversions are performed outside the binding).
- An additional item containing the kWh fee from your electricity supplier can be added also (and it can be dynamic).

If you want electricity tax included in your total price, please add either `electricity-tax` or `reduced-electricity-tax` to the group - depending on which one applies.
See [Electricity Tax](#electricity-tax) for further information.
Expand Down Expand Up @@ -141,6 +143,17 @@ This reduced rate is made available through channel `reduced-electricity-tax`.
The binding cannot determine or manage rate variations as they depend on metering data.
Usually `reduced-electricity-tax` is preferred when using electricity for heating.

#### CO₂ Emissions

Data for the CO₂ emission channels is published as time series with a resolution of 5 minutes.

Channel `co2-emission-realtime` provides near up-to-date historic emission and is refreshed every 5 minutes.
When the binding is started, or a new item is linked, or a linked item receives an update command, historic data for the last 24 hours is provided in addition to the current value.

Channel `co2-emission-prognosis` provides estimated prognosis for future emissions and is refreshed every 15 minutes.
Depending on the time of the day, an update of the prognosis may include estimates for more than 9 hours, but every update will have at least 9 hours into the future.
A persistence configuration is required for this channel.

## Thing Actions

Thing actions can be used to perform calculations as well as import prices directly into rules without relying on persistence.
Expand Down
Loading

0 comments on commit 5cb2d06

Please sign in to comment.