2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.openwebnet.handler;
15 import static org.openhab.binding.openwebnet.OpenWebNetBindingConstants.CHANNEL_POWER;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
21 import javax.measure.quantity.Power;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.openwebnet.OpenWebNetBindingConstants;
26 import org.openhab.core.library.types.QuantityType;
27 import org.openhab.core.library.unit.Units;
28 import org.openhab.core.thing.ChannelUID;
29 import org.openhab.core.thing.Thing;
30 import org.openhab.core.thing.ThingStatus;
31 import org.openhab.core.thing.ThingStatusInfo;
32 import org.openhab.core.thing.ThingTypeUID;
33 import org.openhab.core.types.Command;
34 import org.openhab.core.types.UnDefType;
35 import org.openwebnet4j.OpenGateway;
36 import org.openwebnet4j.communication.OWNException;
37 import org.openwebnet4j.message.BaseOpenMessage;
38 import org.openwebnet4j.message.EnergyManagement;
39 import org.openwebnet4j.message.EnergyManagement.DIM;
40 import org.openwebnet4j.message.FrameException;
41 import org.openwebnet4j.message.Where;
42 import org.openwebnet4j.message.WhereEnergyManagement;
43 import org.openwebnet4j.message.Who;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * The {@link OpenWebNetEnergyHandler} is responsible for handling commands/messages for a Energy Management OpenWebNet
49 * device. It extends the abstract {@link OpenWebNetThingHandler}.
51 * @author Massimo Valla - Initial contribution
52 * @author Andrea Conte - Energy management
55 public class OpenWebNetEnergyHandler extends OpenWebNetThingHandler {
57 private final Logger logger = LoggerFactory.getLogger(OpenWebNetEnergyHandler.class);
59 public final static Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.ENERGY_MANAGEMENT_SUPPORTED_THING_TYPES;
60 public final int ENERGY_SUBSCRIPTION_PERIOD = 10; // minutes
61 private @Nullable ScheduledFuture<?> notificationSchedule;
63 public OpenWebNetEnergyHandler(Thing thing) {
67 public Boolean isFirstSchedulerLaunch = true;
70 public void initialize() {
73 // In order to get data from the probe we must send a command over the bus, this could be done only when the
75 // a) usual flow: binding is starting, the bridge isn't online (startup flow not yet completed) --> subscriber
76 // can't be started here, it will be started inside the bridgeStatusChanged event.
77 // b) thing's discovery: binding is up and running, the bridge is online --> subscriber must be started here
78 // otherwise data will be read only after a reboot.
80 OpenWebNetBridgeHandler h = bridgeHandler;
81 if (h != null && h.isBusGateway()) {
82 OpenGateway gw = h.gateway;
83 if (gw != null && gw.isConnected()) {
85 subscribeToActivePowerChanges();
91 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
92 super.bridgeStatusChanged(bridgeStatusInfo);
94 // subscribe the scheduler only after the bridge is online
95 if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
96 subscribeToActivePowerChanges();
100 private void subscribeToActivePowerChanges() {
101 notificationSchedule = scheduler.scheduleAtFixedRate(() -> {
102 if (isFirstSchedulerLaunch) {
104 "subscribeToActivePowerChanges() For WHERE={} subscribing to active power changes notification for the next {}min",
105 deviceWhere, ENERGY_SUBSCRIPTION_PERIOD);
108 "subscribeToActivePowerChanges() Refreshing subscription for the next {}min for WHERE={} to active power changes notification",
109 ENERGY_SUBSCRIPTION_PERIOD, deviceWhere);
113 bridgeHandler.gateway.send(EnergyManagement.setActivePowerNotificationsTime(deviceWhere.value(),
114 ENERGY_SUBSCRIPTION_PERIOD));
115 isFirstSchedulerLaunch = false;
116 } catch (Exception e) {
117 if (isFirstSchedulerLaunch) {
119 "subscribeToActivePowerChanges() For WHERE={} could not subscribe to active power changes notifications. Exception={}",
120 deviceWhere, e.getMessage());
123 "subscribeToActivePowerChanges() Unable to refresh subscription to active power changes notifications for WHERE={}. Exception={}",
124 deviceWhere, e.getMessage());
127 }, 0, ENERGY_SUBSCRIPTION_PERIOD - 1, TimeUnit.MINUTES);
131 public void dispose() {
132 if (notificationSchedule != null) {
133 logger.debug("dispose() scheduler stopped.");
135 notificationSchedule.cancel(false);
141 protected Where buildBusWhere(String wStr) throws IllegalArgumentException {
142 return new WhereEnergyManagement(wStr);
146 protected void requestChannelState(ChannelUID channel) {
147 logger.debug("requestChannelState() thingUID={} channel={}", thing.getUID(), channel.getId());
148 Where w = deviceWhere;
151 send(EnergyManagement.requestActivePower(w.value()));
152 } catch (OWNException e) {
153 logger.debug("Exception while requesting channel {} state: {}", channel, e.getMessage(), e);
156 logger.warn("Could not requestChannelState(): deviceWhere is null");
161 protected void refreshDevice(boolean refreshAll) {
162 requestChannelState(new ChannelUID("any:any:any:any"));
166 protected void handleChannelCommand(ChannelUID channel, Command command) {
167 logger.warn("handleChannelCommand() Read only channel, unsupported command {}", command);
171 protected String ownIdPrefix() {
172 return Who.ENERGY_MANAGEMENT.value().toString();
176 protected void handleMessage(BaseOpenMessage msg) {
177 super.handleMessage(msg);
179 if (msg.isCommand()) {
180 logger.warn("handleMessage() Ignoring unsupported command for thing {}. Frame={}", getThing().getUID(),
184 // fix: check for correct DIM (ActivePower / 113)
185 if (msg.getDim().equals(DIM.ACTIVE_POWER))
186 updateActivePower(msg);
188 logger.debug("handleMessage() Ignoring message {} because it's not related to active power value.",
194 * Updates energy power state based on a EnergyManagement message received from the OWN network
196 * @param msg the EnergyManagement message received
197 * @throws FrameException
199 private void updateActivePower(BaseOpenMessage msg) {
202 activePower = Integer.parseInt(msg.getDimValues()[0]);
203 updateState(CHANNEL_POWER, new QuantityType<Power>(activePower, Units.WATT));
204 } catch (FrameException e) {
205 logger.warn("FrameException on frame {}: {}", msg, e.getMessage());
206 updateState(CHANNEL_POWER, UnDefType.UNDEF);