2 * Copyright (c) 2010-2023 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.jeelink.internal.pca301;
15 import static org.openhab.binding.jeelink.internal.JeeLinkBindingConstants.*;
17 import java.math.BigDecimal;
18 import java.math.RoundingMode;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
21 import java.util.concurrent.atomic.AtomicInteger;
23 import org.openhab.binding.jeelink.internal.JeeLinkHandler;
24 import org.openhab.binding.jeelink.internal.JeeLinkSensorHandler;
25 import org.openhab.binding.jeelink.internal.ReadingPublisher;
26 import org.openhab.binding.jeelink.internal.config.Pca301SensorConfig;
27 import org.openhab.core.library.types.OnOffType;
28 import org.openhab.core.library.types.QuantityType;
29 import org.openhab.core.library.unit.Units;
30 import org.openhab.core.thing.ChannelUID;
31 import org.openhab.core.thing.Thing;
32 import org.openhab.core.types.Command;
33 import org.openhab.core.types.RefreshType;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
38 * Handler for an EC3000 sensor thing.
40 * @author Volker Bier - Initial contribution
42 public class Pca301SensorHandler extends JeeLinkSensorHandler<Pca301Reading> {
43 private final Logger logger = LoggerFactory.getLogger(Pca301SensorHandler.class);
45 private JeeLinkHandler bridge;
46 private OnOffType state;
47 private final AtomicInteger channel = new AtomicInteger(-1);
49 private ScheduledFuture<?> retry;
50 private int sendCount;
52 public Pca301SensorHandler(Thing thing, String sensorType) {
53 super(thing, sensorType);
57 public Class<Pca301Reading> getReadingClass() {
58 return Pca301Reading.class;
62 public void initialize() {
65 bridge = (JeeLinkHandler) getBridge().getHandler();
67 Pca301SensorConfig cfg = getConfigAs(Pca301SensorConfig.class);
68 sendCount = cfg.sendCount;
70 logger.debug("initilized handler for thing {} ({}): sendCount = {}", getThing().getLabel(),
71 getThing().getUID().getId(), sendCount);
75 public void dispose() {
81 public synchronized void handleCommand(ChannelUID channelUid, Command command) {
82 logger.debug("received command for thing {} ({}): {}", getThing().getLabel(), getThing().getUID().getId(),
85 if (channelUid.getIdWithoutGroup().equals(SWITCHING_STATE_CHANNEL)) {
86 if (command instanceof OnOffType onOffCommand) {
87 sendCommandRetry(onOffCommand);
91 } else if (command != RefreshType.REFRESH) {
92 logger.warn("Unsupported command {} for channel {} of sensor with id {}.", command,
93 channelUid.getIdWithoutGroup(), this.id);
98 public ReadingPublisher<Pca301Reading> createPublisher() {
99 return new ReadingPublisher<Pca301Reading>() {
101 public void publish(Pca301Reading reading) {
102 if (reading != null) {
103 channel.set(reading.getChannel());
105 BigDecimal current = new BigDecimal(reading.getCurrent()).setScale(1, RoundingMode.HALF_UP);
106 state = OnOffType.from(reading.isOn());
108 updateState(CURRENT_POWER_CHANNEL, new QuantityType<>(current, Units.WATT));
109 updateState(CONSUMPTION_CHANNEL, new QuantityType<>(reading.getTotal(), Units.WATT_HOUR));
110 updateState(SWITCHING_STATE_CHANNEL, state);
112 logger.debug("updated states for thing {} ({}): state={}, current={}, total={}",
113 getThing().getLabel(), getThing().getUID().getId(), state, current, reading.getTotal());
118 public void dispose() {
123 private void sendCommand(Command command) {
124 int chan = channel.get();
127 if (command == RefreshType.REFRESH) {
128 bridge.getConnection().sendCommands(chan + ",4," + id.replaceAll("-", ",") + ",0,255,255,255,255s");
129 } else if (command instanceof OnOffType) {
130 bridge.getConnection().sendCommands(chan + ",5," + id.replaceAll("-", ",") + ","
131 + (command == OnOffType.ON ? "1" : "2") + ",255,255,255,255s");
133 logger.warn("Unsupported command {} for sensor with id {}.", command, this.id);
135 } else if (command != RefreshType.REFRESH && !(command instanceof OnOffType)) {
136 logger.warn("Could not send command {} for sensor with id {}. Ignoring command.", command, this.id);
140 private synchronized void sendCommandRetry(OnOffType command) {
143 retry = scheduler.scheduleWithFixedDelay(new Runnable() {
144 int remainingRetries = sendCount;
149 logger.debug("skip sending of command (current state not yet known) for thing {} ({}): {}",
150 getThing().getLabel(), getThing().getUID().getId(), command);
151 } else if ((state != command && remainingRetries > 0)) {
152 logger.debug("sending command for thing {} ({}) attempt {}/{}: {}", getThing().getLabel(),
153 getThing().getUID().getId(), (sendCount - remainingRetries + 1), sendCount, command);
155 sendCommand(command);
158 // we get here when the state is as expected or when the state is still not as expected after
159 // the configured number of retries. we should cancel the retry for both cases
160 if (state != command) {
161 logger.debug("giving up command for thing {} ({}): {}", getThing().getLabel(),
162 getThing().getUID().getId(), command);
168 }, 0, 2, TimeUnit.SECONDS);
171 private synchronized void cancelRetry() {