Skip to content

Commit 4fc8f75

Browse files
authored
[Senseenergy] Initial contribution of SenseEnergy Binding (openhab#18244)
* Initial contribution of SenseEnergy Binding Signed-off-by: Jeff James <[email protected]>
1 parent 7b67b1c commit 4fc8f75

File tree

63 files changed

+7287
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+7287
-0
lines changed

CODEOWNERS

+1
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@
338338
/bundles/org.openhab.binding.senechome/ @vctender @KorbinianP @eguib
339339
/bundles/org.openhab.binding.seneye/ @nikotanghe
340340
/bundles/org.openhab.binding.sensebox/ @hakan42
341+
/bundles/org.openhab.binding.senseenergy/ @jsjames
341342
/bundles/org.openhab.binding.sensibo/ @seime
342343
/bundles/org.openhab.binding.sensorcommunity/ @weymann
343344
/bundles/org.openhab.binding.serial/ @MikeJMajor

bom/openhab-addons/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -1666,6 +1666,11 @@
16661666
<artifactId>org.openhab.binding.sensebox</artifactId>
16671667
<version>${project.version}</version>
16681668
</dependency>
1669+
<dependency>
1670+
<groupId>org.openhab.addons.bundles</groupId>
1671+
<artifactId>org.openhab.binding.senseenergy</artifactId>
1672+
<version>${project.version}</version>
1673+
</dependency>
16691674
<dependency>
16701675
<groupId>org.openhab.addons.bundles</groupId>
16711676
<artifactId>org.openhab.binding.sensibo</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
This content is produced and maintained by the openHAB project.
2+
3+
* Project home: https://www.openhab.org
4+
5+
== Declared Project Licenses
6+
7+
This program and the accompanying materials are made available under the terms
8+
of the Eclipse Public License 2.0 which is available at
9+
https://www.eclipse.org/legal/epl-2.0/.
10+
11+
== Source Code
12+
13+
https://github.com/openhab/openhab-addons

bundles/org.openhab.binding.senseenergy/README.md

+243
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>org.openhab.addons.bundles</groupId>
9+
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
10+
<version>5.0.0-SNAPSHOT</version>
11+
</parent>
12+
13+
<artifactId>org.openhab.binding.senseenergy</artifactId>
14+
15+
<name>openHAB Add-ons :: Bundles :: SenseEnergy Binding</name>
16+
17+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<features name="org.openhab.binding.senseenergy-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
3+
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
4+
5+
<feature name="openhab-binding-senseenergy" description="SenseEnergy Binding" version="${project.version}">
6+
<feature>openhab-runtime-base</feature>
7+
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.senseenergy/${project.version}</bundle>
8+
</feature>
9+
</features>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright (c) 2010-2025 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.senseenergy.internal;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
import org.openhab.core.thing.ThingTypeUID;
17+
import org.openhab.core.thing.type.ChannelGroupTypeUID;
18+
19+
/**
20+
* The {@link SenseEnergyBindingConstants} class defines common constants, which are
21+
* used across the whole binding.
22+
*
23+
* @author Jeff James - Initial contribution
24+
*/
25+
@NonNullByDefault
26+
public class SenseEnergyBindingConstants {
27+
private static final String BINDING_ID = "senseenergy";
28+
29+
// List of all Thing Type UIDs
30+
public static final ThingTypeUID APIBRIDGE_THING_TYPE = new ThingTypeUID(BINDING_ID, "cloud-connector");
31+
public static final ThingTypeUID MONITOR_THING_TYPE = new ThingTypeUID(BINDING_ID, "monitor");
32+
public static final ThingTypeUID PROXY_DEVICE_THING_TYPE = new ThingTypeUID(BINDING_ID, "proxy-device");
33+
34+
public static final String PARAM_MONITOR_ID = "id";
35+
36+
public static final int HEARTBEAT_MINUTES = 5;
37+
38+
/** Monitor Bridge/Thing ***/
39+
// Channel group type UIDs
40+
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_DEVICE_TEMPLATE = new ChannelGroupTypeUID(BINDING_ID,
41+
"device-template");
42+
43+
// Channel Groups
44+
public static final String CHANNEL_GROUP_GENERAL = "general";
45+
public static final String CHANNEL_GROUP_DISCOVERED_DEVICES = "discovered-devices";
46+
public static final String CHANNEL_GROUP_SELF_REPORTING_DEVICES = "self-reporting-devices";
47+
public static final String CHANNEL_GROUP_PROXY_DEVICES = "proxy-devices";
48+
49+
// Monitor Channel IDs
50+
public static final String CHANNEL_FREQUENCY = "frequency";
51+
public static final String CHANNEL_GRID_POWER = "grid-power";
52+
public static final String CHANNEL_POTENTIAL_1 = "potential-1";
53+
public static final String CHANNEL_POTENTIAL_2 = "potential-2";
54+
public static final String CHANNEL_LEG_1_POWER = "leg-1-power";
55+
public static final String CHANNEL_LEG_2_POWER = "leg-2-power";
56+
public static final String CHANNEL_MAIN_POWER = "main-power";
57+
public static final String CHANNEL_SOLAR_POWER = "solar-power";
58+
public static final String CHANNEL_DEVICES_UPDATED_TRIGGER = "devices-updated-trigger";
59+
60+
// Discovered Device Channel IDs
61+
public static final String CHANNEL_DEVICE_POWER = "device-power";
62+
public static final String CHANNEL_DEVICE_TRIGGER = "device-trigger";
63+
64+
// Properties
65+
public static final String PROPERTY_MONITOR_SOLAR_CONFIGURED = "solarConfigured";
66+
public static final String PROPERTY_MONITOR_IP_ADDRESS = "ipAddress";
67+
public static final String PROPERTY_MONITOR_VERSION = "version";
68+
public static final String PROPERTY_MONITOR_SERIAL = "serial";
69+
public static final String PROPERTY_MONITOR_SSID = "ssid";
70+
public static final String PROPERTY_MONITOR_MAC = "mac";
71+
72+
/** PROXY DEVICE THING ***/
73+
// Channel IDs
74+
public static final String CHANNEL_PROXY_DEVICE_POWER = "proxy-device-power";
75+
public static final String CHANNEL_PROXY_DEVICE_SWITCH = "proxy-device-switch";
76+
public static final String CHANNEL_PROXY_DEVICE_DIMMER = "proxy-device-dimmer";
77+
public static final String CHANNEL_PROXY_DEVICE_STATE = "proxy-device-state";
78+
79+
public static final String CONFIG_PARAMETER_MAC = "mac";
80+
public static final String CONFIG_PARAMETER_POWER_LEVELS = "powerLevels";
81+
public static final String CONFIG_PARAMETER_SENSE_NAME = "senseName";
82+
83+
public static final String ACTION_OUTPUT_CONSUMPTION = "consumption";
84+
public static final String ACTION_OUTPUT_PRODUCTION = "production";
85+
public static final String ACTION_OUTPUT_FROM_GRID = "fromGrid";
86+
public static final String ACTION_OUTPUT_TO_GRID = "toGrid";
87+
public static final String ACTION_OUTPUT_NET_PRODUCTION = "netProduction";
88+
public static final String ACTION_OUTPUT_SOLAR_POWERED = "solarPowered";
89+
public static final String ACTION_INPUT_SCALE = "scale";
90+
public static final String ACTION_INPUT_DATETIME = "datetime";
91+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright (c) 2010-2025 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.senseenergy.internal;
14+
15+
import static org.openhab.binding.senseenergy.internal.SenseEnergyBindingConstants.*;
16+
17+
import java.util.Set;
18+
19+
import org.eclipse.jdt.annotation.NonNullByDefault;
20+
import org.eclipse.jdt.annotation.Nullable;
21+
import org.openhab.binding.senseenergy.internal.handler.SenseEnergyBridgeHandler;
22+
import org.openhab.binding.senseenergy.internal.handler.SenseEnergyMonitorHandler;
23+
import org.openhab.binding.senseenergy.internal.handler.SenseEnergyProxyDeviceHandler;
24+
import org.openhab.core.io.net.http.HttpClientFactory;
25+
import org.openhab.core.io.net.http.WebSocketFactory;
26+
import org.openhab.core.thing.Bridge;
27+
import org.openhab.core.thing.Thing;
28+
import org.openhab.core.thing.ThingTypeUID;
29+
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
30+
import org.openhab.core.thing.binding.ThingHandler;
31+
import org.openhab.core.thing.binding.ThingHandlerFactory;
32+
import org.openhab.core.thing.type.ChannelGroupTypeRegistry;
33+
import org.openhab.core.thing.type.ChannelTypeRegistry;
34+
import org.osgi.service.component.annotations.Activate;
35+
import org.osgi.service.component.annotations.Component;
36+
import org.osgi.service.component.annotations.Reference;
37+
38+
/**
39+
* The {@link SenseEnergyHandlerFactory}
40+
*
41+
* @author Jeff James - Initial contribution
42+
*/
43+
@NonNullByDefault
44+
@Component(configurationPid = "binding.senseenergy", service = ThingHandlerFactory.class)
45+
public class SenseEnergyHandlerFactory extends BaseThingHandlerFactory {
46+
47+
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(APIBRIDGE_THING_TYPE, MONITOR_THING_TYPE,
48+
PROXY_DEVICE_THING_TYPE);
49+
50+
private final HttpClientFactory httpClientFactory;
51+
private final WebSocketFactory webSocketFactory;
52+
private final ChannelGroupTypeRegistry channelGroupTypeRegistry;
53+
private final ChannelTypeRegistry channelTypeRegistry;
54+
55+
@Activate
56+
public SenseEnergyHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
57+
final @Reference WebSocketFactory webSocketFactory,
58+
final @Reference ChannelGroupTypeRegistry channelGroupTypeRegistry,
59+
final @Reference ChannelTypeRegistry channelTypeRegistry) {
60+
this.httpClientFactory = httpClientFactory;
61+
this.webSocketFactory = webSocketFactory;
62+
this.channelGroupTypeRegistry = channelGroupTypeRegistry;
63+
this.channelTypeRegistry = channelTypeRegistry;
64+
}
65+
66+
@Override
67+
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
68+
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
69+
}
70+
71+
@Override
72+
protected @Nullable ThingHandler createHandler(Thing thing) {
73+
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
74+
75+
if (APIBRIDGE_THING_TYPE.equals(thingTypeUID)) {
76+
return new SenseEnergyBridgeHandler((Bridge) thing, this.httpClientFactory.getCommonHttpClient());
77+
} else if (MONITOR_THING_TYPE.equals(thingTypeUID)) {
78+
return new SenseEnergyMonitorHandler((Bridge) thing, webSocketFactory.getCommonWebSocketClient(),
79+
channelGroupTypeRegistry, channelTypeRegistry);
80+
} else if (PROXY_DEVICE_THING_TYPE.equals(thingTypeUID)) {
81+
return new SenseEnergyProxyDeviceHandler(thing);
82+
}
83+
84+
return null;
85+
}
86+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright (c) 2010-2025 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.senseenergy.internal.actions;
14+
15+
import static org.openhab.binding.senseenergy.internal.SenseEnergyBindingConstants.*;
16+
17+
import java.time.Instant;
18+
import java.util.Collections;
19+
import java.util.HashMap;
20+
import java.util.Map;
21+
import java.util.concurrent.ExecutionException;
22+
import java.util.concurrent.TimeoutException;
23+
24+
import javax.measure.quantity.Dimensionless;
25+
import javax.measure.quantity.Energy;
26+
27+
import org.eclipse.jdt.annotation.NonNullByDefault;
28+
import org.eclipse.jdt.annotation.Nullable;
29+
import org.openhab.binding.senseenergy.internal.api.SenseEnergyApi;
30+
import org.openhab.binding.senseenergy.internal.api.SenseEnergyApiException;
31+
import org.openhab.binding.senseenergy.internal.api.dto.SenseEnergyApiGetTrends;
32+
import org.openhab.binding.senseenergy.internal.handler.SenseEnergyMonitorHandler;
33+
import org.openhab.core.automation.annotation.ActionInput;
34+
import org.openhab.core.automation.annotation.ActionOutput;
35+
import org.openhab.core.automation.annotation.RuleAction;
36+
import org.openhab.core.library.types.QuantityType;
37+
import org.openhab.core.library.unit.Units;
38+
import org.openhab.core.thing.binding.ThingActions;
39+
import org.openhab.core.thing.binding.ThingActionsScope;
40+
import org.openhab.core.thing.binding.ThingHandler;
41+
import org.osgi.service.component.annotations.Component;
42+
import org.osgi.service.component.annotations.ServiceScope;
43+
import org.slf4j.Logger;
44+
import org.slf4j.LoggerFactory;
45+
46+
/**
47+
* The { @link SenseEnergyMonitorActions } class implements the action(s) methods for the binding.
48+
*
49+
* @author Jeff James - Initial contribution
50+
*/
51+
@Component(scope = ServiceScope.PROTOTYPE, service = SenseEnergyMonitorActions.class)
52+
@ThingActionsScope(name = "senseenergy")
53+
@NonNullByDefault
54+
public class SenseEnergyMonitorActions implements ThingActions {
55+
private final Logger logger = LoggerFactory.getLogger(SenseEnergyMonitorActions.class);
56+
57+
private @Nullable SenseEnergyMonitorHandler deviceHandler;
58+
59+
@Override
60+
public void setThingHandler(@Nullable ThingHandler handler) {
61+
if (handler instanceof SenseEnergyMonitorHandler deviceHandler) {
62+
this.deviceHandler = deviceHandler;
63+
}
64+
}
65+
66+
@Override
67+
public @Nullable ThingHandler getThingHandler() {
68+
return deviceHandler;
69+
}
70+
71+
/*
72+
* Query water usage
73+
*/
74+
@RuleAction(label = "Query Energy Trend", description = "@text/actions.description.query-energy-trend")
75+
public @ActionOutput(name = ACTION_OUTPUT_CONSUMPTION, type = "QuantityType<Energy>", description = "@text/actions.output.description.consumption") //
76+
@ActionOutput(name = ACTION_OUTPUT_PRODUCTION, type = "QuantityType<Energy>", description = "@text/actions.output.description.production") //
77+
@ActionOutput(name = ACTION_OUTPUT_FROM_GRID, type = "QuantityType<Energy>", description = "@text/actions.output.description.from-grid") //
78+
@ActionOutput(name = ACTION_OUTPUT_TO_GRID, type = "QuantityType<Energy>", description = "@text/actions.output.description.to-grid") //
79+
@ActionOutput(name = ACTION_OUTPUT_NET_PRODUCTION, type = "QuantityType<Energy>", description = "@text/actions.output.description.net-production") //
80+
@ActionOutput(name = ACTION_OUTPUT_SOLAR_POWERED, type = "QuantityType<Dimensionless>", description = "@text/actions.output.description.solar-powered") //
81+
Map<String, QuantityType<?>> queryEnergyTrend( //
82+
@ActionInput(name = ACTION_INPUT_SCALE, label = "Scale", required = true, description = "@text/actions.input.description.scale") @Nullable String scale, //
83+
@ActionInput(name = ACTION_INPUT_DATETIME, label = "Date/Time", required = true, description = "@text/actions.input.description.datetime") @Nullable Instant datetime) {
84+
logger.info("queryEnergyTrend called");
85+
86+
SenseEnergyMonitorHandler localDeviceHandler = deviceHandler;
87+
if (localDeviceHandler == null) {
88+
logger.warn("querying device usage, but device is undefined.");
89+
return Collections.emptyMap();
90+
}
91+
92+
Instant localDateTime = (datetime == null) ? Instant.now() : datetime;
93+
94+
if (scale == null) {
95+
logger.warn("queryEnergyTrends called with null inputs");
96+
return Collections.emptyMap();
97+
}
98+
99+
if (!SenseEnergyApi.TrendScale.contains(scale)) {
100+
logger.warn("Invalid scale type in call to queryEnergyTrend");
101+
return Collections.emptyMap();
102+
}
103+
SenseEnergyApi.TrendScale trendScale = SenseEnergyApi.TrendScale.valueOf(scale);
104+
105+
SenseEnergyApiGetTrends trends;
106+
try {
107+
trends = localDeviceHandler.getApi().getTrendData(localDeviceHandler.getId(), trendScale, localDateTime);
108+
} catch (InterruptedException | TimeoutException | ExecutionException | SenseEnergyApiException e) {
109+
logger.warn("queryEnergyTrends function failed - {}", e.getMessage());
110+
return Collections.emptyMap();
111+
}
112+
113+
if (trends == null) {
114+
return Collections.emptyMap();
115+
}
116+
117+
Map<String, QuantityType<?>> valuesMap = new HashMap<>();
118+
119+
valuesMap.put(ACTION_OUTPUT_CONSUMPTION,
120+
new QuantityType<Energy>(trends.consumption.totalPower, Units.KILOWATT_HOUR));
121+
valuesMap.put(ACTION_OUTPUT_PRODUCTION,
122+
new QuantityType<Energy>(trends.production.totalPower, Units.KILOWATT_HOUR));
123+
valuesMap.put(ACTION_OUTPUT_TO_GRID, new QuantityType<Energy>(trends.toGridEnergy, Units.KILOWATT_HOUR));
124+
valuesMap.put(ACTION_OUTPUT_FROM_GRID, new QuantityType<Energy>(trends.fromGridEnergy, Units.KILOWATT_HOUR));
125+
valuesMap.put(ACTION_OUTPUT_NET_PRODUCTION,
126+
new QuantityType<Energy>(trends.netProduction, Units.KILOWATT_HOUR));
127+
valuesMap.put(ACTION_OUTPUT_SOLAR_POWERED, new QuantityType<Dimensionless>(trends.solarPowered, Units.PERCENT));
128+
129+
return valuesMap;
130+
}
131+
132+
// Static method for Rules DSL backward compatibility
133+
public static @Nullable Map<String, QuantityType<?>> queryEnergyTrend(ThingActions actions, @Nullable String scale,
134+
@Nullable Instant datetime) {
135+
if (actions instanceof SenseEnergyMonitorActions localActions) {
136+
return localActions.queryEnergyTrend(scale, datetime);
137+
} else {
138+
throw new IllegalArgumentException("Instance is not a SenseEnergyMonitorActions class.");
139+
}
140+
}
141+
}

0 commit comments

Comments
 (0)