org.openhab.binding.keba.handler.KeContactP20Handler.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.binding.keba.handler.KeContactP20Handler.java

Source

/**
 * Copyright (c) 2014 openHAB UG (haftungsbeschraenkt) and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.openhab.binding.keba.handler;

import static org.openhab.binding.keba.KebaBindingConstants.*;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.PortUnreachableException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.DatagramChannel;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Iterator;
import java.util.Map;
import java.util.TimeZone;
import java.util.Map.Entry;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.lang.StringUtils;
import org.eclipse.smarthome.core.library.types.DateTimeType;
import org.eclipse.smarthome.core.library.types.DecimalType;
import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType;
import org.eclipse.smarthome.core.library.types.OnOffType;
import org.eclipse.smarthome.core.library.types.PercentType;
import org.eclipse.smarthome.core.thing.ChannelUID;
import org.eclipse.smarthome.core.thing.Thing;
import org.eclipse.smarthome.core.thing.ThingStatus;
import org.eclipse.smarthome.core.thing.ThingStatusDetail;
import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
import org.eclipse.smarthome.core.types.Command;
import org.eclipse.smarthome.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;

/**
 * The {@link KeContactP20Handler} is responsible for handling commands, which
 * are sent to one of the channels.
 * 
 * @author Karel Goderis - Initial contribution
 */
public class KeContactP20Handler extends BaseThingHandler {

    public static final String IP_ADDRESS = "ipAddress";
    public static final String POLLING_REFRESH_INTERVAL = "refreshInterval";

    public static final int CONNECTION_REFRESH_INTERVAL = 100;
    public static final int REPORT_INTERVAL = 2000;
    public static final int PING_TIME_OUT = 3000;
    public static final int BUFFER_SIZE = 1024;
    public static final int REMOTE_PORT_NUMBER = 7090;
    public static final int LISTENER_PORT_NUMBER = 7090;

    private Logger logger = LoggerFactory.getLogger(KeContactP20Handler.class);

    private Selector selector;
    private DatagramChannel datagramChannel = null;
    protected SelectionKey datagramChannelKey = null;
    protected DatagramChannel listenerChannel = null;
    protected SelectionKey listenerKey = null;
    private final Lock lock = new ReentrantLock();
    protected JsonParser parser = new JsonParser();

    private ScheduledFuture<?> listeningJob;
    private ScheduledFuture<?> pollingJob;

    private int maxPresetCurrent = 0;
    private int maxSystemCurrent = 63000;

    public KeContactP20Handler(Thing thing) {
        super(thing);
    }

    @Override
    public void initialize() {
        logger.debug("Initializing KEBA KeContact P20 handler.");

        try {
            selector = Selector.open();
        } catch (IOException e) {
            logger.error("An exception occurred while registering the selector: '{}'", e.getMessage());
        }

        configureListener(LISTENER_PORT_NUMBER);

        if (getConfig().get(IP_ADDRESS) != null && !getConfig().get(IP_ADDRESS).equals("")) {

            establishConnection();

            if (listeningJob == null || listeningJob.isCancelled()) {
                try {
                    listeningJob = scheduler.scheduleWithFixedDelay(listeningRunnable, 0,
                            CONNECTION_REFRESH_INTERVAL, TimeUnit.MILLISECONDS);
                } catch (Exception e) {
                    updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
                            "An exception occurred while scheduling the connection job");
                }
            }
            if (pollingJob == null || pollingJob.isCancelled()) {
                try {
                    pollingJob = scheduler.scheduleWithFixedDelay(pollingRunnable, 0,
                            ((BigDecimal) getConfig().get(POLLING_REFRESH_INTERVAL)).intValue(), TimeUnit.SECONDS);
                } catch (Exception e) {
                    updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
                            "An exception occurred while scheduling the polling job");
                }
            }
        } else {
            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
                    "IP address or port number not set");
        }

    }

    @Override
    public void dispose() {

        try {
            selector.close();
        } catch (IOException e) {
            logger.error("An exception occurred while closing the selector: '{}'", e.getMessage());
        }

        try {
            datagramChannel.close();
        } catch (IOException e) {
            logger.warn("An exception occurred while closing the channel '{}': {}", datagramChannel,
                    e.getMessage());
        }

        try {
            listenerChannel.close();
        } catch (IOException e) {
            logger.error("An exception occurred while closing the listener channel on port number {} ({})",
                    LISTENER_PORT_NUMBER, e.getMessage());
        }

        if (listeningJob != null && !listeningJob.isCancelled()) {
            listeningJob.cancel(true);
            listeningJob = null;
        }

        if (pollingJob != null && !pollingJob.isCancelled()) {
            pollingJob.cancel(true);
            pollingJob = null;
        }

        logger.debug("Handler disposed.");
    }

    protected void configureListener(int listenerPort) {

        // open the listener port
        try {
            listenerChannel = DatagramChannel.open();
            listenerChannel.socket().bind(new InetSocketAddress(listenerPort));
            listenerChannel.configureBlocking(false);

            logger.info("Listening for incoming data on {}", listenerChannel.getLocalAddress());

            synchronized (selector) {
                selector.wakeup();
                try {
                    listenerKey = listenerChannel.register(selector, listenerChannel.validOps());

                } catch (ClosedChannelException e1) {
                    logger.error("An exception occurred while registering a selector: {}", e1.getMessage());
                }
            }
        } catch (Exception e2) {
            logger.error("An exception occurred while creating the Listener Channel on port number {} ({})",
                    listenerPort, e2.getMessage());
        }
    }

    protected ByteBuffer onReadable(DatagramChannel theChannel, int bufferSize,
            InetAddress permittedClientAddress) {
        lock.lock();
        try {

            SelectionKey theSelectionKey = theChannel.keyFor(selector);

            if (theSelectionKey != null) {

                synchronized (selector) {
                    try {
                        selector.selectNow();
                    } catch (IOException e) {
                        logger.error("An exception occurred while selecting: {}", e.getMessage());
                    }
                }

                Iterator<SelectionKey> it = selector.selectedKeys().iterator();
                while (it.hasNext()) {
                    SelectionKey selKey = (SelectionKey) it.next();
                    it.remove();
                    if (selKey.isValid() && selKey.isReadable() && selKey == theSelectionKey) {

                        ByteBuffer readBuffer = ByteBuffer.allocate(bufferSize);
                        int numberBytesRead = 0;
                        boolean error = false;

                        if (selKey == listenerKey) {
                            try {
                                InetSocketAddress clientAddress = (InetSocketAddress) theChannel
                                        .receive(readBuffer);
                                if (clientAddress.getAddress().equals(permittedClientAddress)) {
                                    logger.debug("Received {} on the listener port from {}",
                                            new String(readBuffer.array()), clientAddress);
                                    numberBytesRead = readBuffer.position();
                                } else {
                                    logger.warn(
                                            "Received data from '{}' which is not the permitted remote address '{}'",
                                            clientAddress, permittedClientAddress);
                                    return null;
                                }
                            } catch (Exception e) {
                                logger.error(
                                        "An exception occurred while receiving data on the listener port: '{}'",
                                        e.getMessage());
                                error = true;
                            }

                        } else {

                            try {
                                numberBytesRead = theChannel.read(readBuffer);
                            } catch (NotYetConnectedException e) {
                                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
                                        "The remote host is not yet connected");
                                error = true;
                            } catch (PortUnreachableException e) {
                                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
                                        "The remote host is probably not a KEBA EV Charging station");
                                error = true;
                            } catch (IOException e) {
                                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
                                        "An IO exception occurred");
                                error = true;
                            }
                        }

                        if (numberBytesRead == -1) {
                            error = true;
                        }

                        if (error) {
                            logger.debug("Disconnecting '{}' because of a socket error",
                                    getThing().getUID().toString());
                            try {
                                theChannel.close();
                            } catch (IOException e) {
                                logger.error("An exception occurred while closing the channel '{}': {}",
                                        datagramChannel, e.getMessage());
                            }

                            onConnectionLost();

                        } else {
                            readBuffer.flip();
                            return readBuffer;
                        }
                    }
                }
            }

            return null;
        } finally {
            lock.unlock();
        }
    }

    protected void onWritable(ByteBuffer buffer, DatagramChannel theChannel) {
        lock.lock();
        try {

            SelectionKey theSelectionKey = theChannel.keyFor(selector);

            if (theSelectionKey != null) {

                synchronized (selector) {
                    try {
                        selector.selectNow();
                    } catch (IOException e) {
                        logger.error("An exception occurred while selecting: {}", e.getMessage());
                    }
                }

                Iterator<SelectionKey> it = selector.selectedKeys().iterator();
                while (it.hasNext()) {
                    SelectionKey selKey = (SelectionKey) it.next();
                    it.remove();
                    if (selKey.isValid() && selKey.isWritable() && selKey == theSelectionKey) {

                        boolean error = false;
                        buffer.rewind();

                        try {
                            logger.debug("Sending '{}' on the channel '{}'->'{}'",
                                    new Object[] { new String(buffer.array()), theChannel.getLocalAddress(),
                                            theChannel.getRemoteAddress() });
                            theChannel.write(buffer);
                        } catch (NotYetConnectedException e) {
                            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
                                    "The remote host is not yet connected");
                            error = true;
                        } catch (ClosedChannelException e) {
                            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
                                    "The connection to the remote host is closed");
                            error = true;
                        } catch (IOException e) {
                            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
                                    "An IO exception occurred");
                            error = true;
                        }

                        if (error) {
                            logger.debug("Disconnecting '{}' because of a socket error",
                                    getThing().getUID().toString());
                            try {
                                theChannel.close();
                            } catch (IOException e) {
                                logger.warn("An exception occurred while closing the channel '{}': {}",
                                        datagramChannel, e.getMessage());
                            }

                            onConnectionLost();

                        }
                    }
                }
            }
        } finally {
            lock.unlock();
        }
    }

    public void onConnectionLost() {
        establishConnection();
    }

    public void onConnectionResumed() {
        updateStatus(ThingStatus.ONLINE);
    }

    private void establishConnection() {
        lock.lock();
        try {
            if (getThing().getStatusInfo().getStatusDetail() != ThingStatusDetail.CONFIGURATION_ERROR
                    && getConfig().get(IP_ADDRESS) != null && !getConfig().get(IP_ADDRESS).equals("")) {

                try {
                    datagramChannel = DatagramChannel.open();
                } catch (Exception e2) {
                    updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
                            "An exception occurred while opening a datagram channel");
                }

                try {
                    datagramChannel.configureBlocking(false);
                } catch (IOException e2) {
                    updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
                            "An exception occurred while configuring a datagram channel");
                }

                synchronized (selector) {
                    selector.wakeup();
                    int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
                    try {
                        datagramChannelKey = datagramChannel.register(selector, interestSet);
                    } catch (ClosedChannelException e1) {
                        updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
                                "An exception occurred while registering a selector");
                    }

                    InetSocketAddress remoteAddress = new InetSocketAddress((String) getConfig().get(IP_ADDRESS),
                            REMOTE_PORT_NUMBER);

                    try {
                        logger.trace("Connecting the channel for {} ", remoteAddress);
                        datagramChannel.connect(remoteAddress);

                        onConnectionResumed();

                    } catch (Exception e) {
                        logger.error("An exception occurred while connecting connecting to '{}:{}' : {}",
                                new Object[] { (String) getConfig().get(IP_ADDRESS), REMOTE_PORT_NUMBER,
                                        e.getMessage() });
                        updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
                                "An exception occurred while connecting");
                    }
                }
            } else {
                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
                        getThing().getStatusInfo().getDescription());
            }
        } finally {
            lock.unlock();
        }
    }

    private Runnable listeningRunnable = new Runnable() {

        @Override
        public void run() {
            lock.lock();
            try {
                if (getThing().getStatusInfo().getStatusDetail() != ThingStatusDetail.CONFIGURATION_ERROR) {
                    if (datagramChannel == null || !datagramChannel.isConnected()) {
                        updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
                                "The connection is not yet initialized");
                        onConnectionLost();
                    }

                    if (datagramChannel.isConnected()) {

                        long stamp = System.currentTimeMillis();
                        if (!InetAddress.getByName(((String) getConfig().get(IP_ADDRESS)))
                                .isReachable(PING_TIME_OUT)) {
                            logger.debug("Ping timed out after '{}' milliseconds",
                                    System.currentTimeMillis() - stamp);
                            logger.trace("Disconnecting the datagram channel '{}'", datagramChannel);
                            try {
                                datagramChannel.close();
                            } catch (IOException e) {
                                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
                                        "An exception occurred while closing the channel");
                            }

                            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
                                    "A ping timeout occurred");
                            onConnectionLost();
                        } else {
                            ByteBuffer buffer = onReadable(datagramChannel, BUFFER_SIZE, null);
                            if (buffer != null && buffer.remaining() > 0) {
                                onRead(buffer, datagramChannel);
                            }
                        }
                    }

                    ByteBuffer buffer = onReadable(listenerChannel, BUFFER_SIZE,
                            InetAddress.getByName((String) getConfig().get(IP_ADDRESS)));
                    if (buffer != null && buffer.remaining() > 0) {
                        onRead(buffer, listenerChannel);
                    }
                }
            } catch (Exception e) {
                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
                        "An exception occurred while receiving event data from the charging station");
            } finally {
                lock.unlock();
            }
        }
    };

    private Runnable pollingRunnable = new Runnable() {

        @Override
        public void run() {
            try {

                String command = "report 1";

                ByteBuffer byteBuffer = ByteBuffer.allocate(command.getBytes().length);
                try {
                    byteBuffer.put(command.getBytes("ASCII"));
                    onWritable(byteBuffer, datagramChannel);
                } catch (UnsupportedEncodingException | NumberFormatException e) {
                    logger.error("An exception occurred while polling the KEBA KeContact P20 for '{}': {}",
                            getThing().getUID(), e.getMessage());
                }

                Thread.sleep(REPORT_INTERVAL);

                command = "report 2";

                byteBuffer = ByteBuffer.allocate(command.getBytes().length);
                try {
                    byteBuffer.put(command.getBytes("ASCII"));
                    onWritable(byteBuffer, datagramChannel);
                } catch (UnsupportedEncodingException | NumberFormatException e) {
                    logger.error("An exception occurred while polling the KEBA KeContact P20 for '{}': {}",
                            getThing().getUID(), e.getMessage());
                }

                Thread.sleep(REPORT_INTERVAL);

                command = "report 3";

                byteBuffer = ByteBuffer.allocate(command.getBytes().length);
                try {
                    byteBuffer.put(command.getBytes("ASCII"));
                    onWritable(byteBuffer, datagramChannel);
                } catch (UnsupportedEncodingException | NumberFormatException e) {
                    logger.error("An exception occurred while polling the KEBA KeContact P20 for '{}': {}",
                            getThing().getUID(), e.getMessage());
                }

            } catch (Exception e) {
                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
            }
        }
    };

    protected void onRead(ByteBuffer byteBuffer, DatagramChannel datagramChannel) {

        String response = new String(byteBuffer.array(), 0, byteBuffer.limit());
        response = StringUtils.chomp(response);

        if (response.contains("TCH-OK")) {
            // ignore confirmation messages which are not JSON
            return;
        }

        try {
            JsonObject readObject = parser.parse(response).getAsJsonObject();

            for (Entry<String, JsonElement> entry : readObject.entrySet()) {

                switch (entry.getKey()) {
                case "Product": {
                    Map<String, String> properties = editProperties();
                    properties.put(CHANNEL_MODEL, entry.getValue().getAsString());
                    updateProperties(properties);
                    break;
                }
                case "Serial": {
                    Map<String, String> properties = editProperties();
                    properties.put(CHANNEL_SERIAL, entry.getValue().getAsString());
                    updateProperties(properties);
                    break;
                }
                case "Firmware": {
                    Map<String, String> properties = editProperties();
                    properties.put(CHANNEL_FIRMWARE, entry.getValue().getAsString());
                    updateProperties(properties);
                    break;

                }
                case "Plug": {
                    int state = entry.getValue().getAsInt();
                    switch (state) {
                    case 0: {
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_WALLBOX), OnOffType.OFF);
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_VEHICLE), OnOffType.OFF);
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_PLUG_LOCKED), OnOffType.OFF);
                        break;
                    }
                    case 1: {
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_WALLBOX), OnOffType.ON);
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_VEHICLE), OnOffType.OFF);
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_PLUG_LOCKED), OnOffType.OFF);
                        break;
                    }
                    case 3: {
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_WALLBOX), OnOffType.ON);
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_VEHICLE), OnOffType.OFF);
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_PLUG_LOCKED), OnOffType.ON);
                        break;
                    }
                    case 5: {
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_WALLBOX), OnOffType.ON);
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_VEHICLE), OnOffType.ON);
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_PLUG_LOCKED), OnOffType.OFF);
                        break;
                    }
                    case 7: {
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_WALLBOX), OnOffType.ON);
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_VEHICLE), OnOffType.ON);
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_PLUG_LOCKED), OnOffType.ON);
                        break;
                    }
                    }
                    break;
                }
                case "State": {
                    State newState = new DecimalType(entry.getValue().getAsInt());
                    updateState(new ChannelUID(getThing().getUID(), CHANNEL_STATE), newState);
                    break;
                }
                case "Enable sys": {
                    int state = entry.getValue().getAsInt();
                    switch (state) {
                    case 1: {
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_ENABLED), OnOffType.ON);
                        break;
                    }
                    default: {
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_ENABLED), OnOffType.OFF);
                        break;
                    }
                    }
                    break;
                }
                case "Curr HW": {
                    int state = entry.getValue().getAsInt();
                    maxSystemCurrent = state;
                    State newState = new DecimalType(state);
                    updateState(new ChannelUID(getThing().getUID(), CHANNEL_MAX_SYSTEM_CURRENT), newState);
                    if (maxSystemCurrent < maxPresetCurrent) {
                        sendCommand("curr " + String.valueOf(maxSystemCurrent));
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_MAX_PRESET_CURRENT),
                                new DecimalType(maxSystemCurrent));
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_MAX_PRESET_CURRENT_RANGE),
                                new PercentType((maxSystemCurrent - 6000) * 100 / (maxSystemCurrent - 6000)));
                    }
                    break;
                }
                case "Curr user": {
                    int state = entry.getValue().getAsInt();
                    maxPresetCurrent = state;
                    updateState(new ChannelUID(getThing().getUID(), CHANNEL_MAX_PRESET_CURRENT),
                            new DecimalType(state));
                    updateState(new ChannelUID(getThing().getUID(), CHANNEL_MAX_PRESET_CURRENT_RANGE),
                            new PercentType((state - 6000) * 100 / (maxSystemCurrent - 6000)));
                    break;
                }
                case "Curr FS": {
                    int state = entry.getValue().getAsInt();
                    State newState = new DecimalType(state);
                    updateState(new ChannelUID(getThing().getUID(), CHANNEL_FAILSAFE_CURRENT), newState);
                    break;
                }
                case "Output": {
                    int state = entry.getValue().getAsInt();
                    switch (state) {
                    case 1: {
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_OUTPUT), OnOffType.ON);
                        break;
                    }
                    default: {
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_OUTPUT), OnOffType.OFF);
                        break;
                    }
                    }
                    break;
                }
                case "Input": {
                    int state = entry.getValue().getAsInt();
                    switch (state) {
                    case 1: {
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_INPUT), OnOffType.ON);
                        break;
                    }
                    default: {
                        updateState(new ChannelUID(getThing().getUID(), CHANNEL_INPUT), OnOffType.OFF);
                        break;
                    }
                    }
                    break;
                }
                case "Sec": {
                    long state = entry.getValue().getAsLong();

                    Calendar uptime = Calendar.getInstance();
                    uptime.setTimeZone(TimeZone.getTimeZone("GMT"));
                    uptime.setTimeInMillis(state * 1000);
                    SimpleDateFormat pFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
                    pFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));

                    updateState(new ChannelUID(getThing().getUID(), CHANNEL_UPTIME),
                            new DateTimeType(pFormatter.format(uptime.getTime())));
                    break;
                }
                case "U1": {
                    int state = entry.getValue().getAsInt();
                    State newState = new DecimalType(state);
                    updateState(new ChannelUID(getThing().getUID(), CHANNEL_U1), newState);
                    break;
                }
                case "U2": {
                    int state = entry.getValue().getAsInt();
                    State newState = new DecimalType(state);
                    updateState(new ChannelUID(getThing().getUID(), CHANNEL_U2), newState);
                    break;
                }
                case "U3": {
                    int state = entry.getValue().getAsInt();
                    State newState = new DecimalType(state);
                    updateState(new ChannelUID(getThing().getUID(), CHANNEL_U3), newState);
                    break;
                }
                case "I1": {
                    int state = entry.getValue().getAsInt();
                    State newState = new DecimalType(state / 1000);
                    updateState(new ChannelUID(getThing().getUID(), CHANNEL_I1), newState);
                    break;
                }
                case "I2": {
                    int state = entry.getValue().getAsInt();
                    State newState = new DecimalType(state / 1000);
                    updateState(new ChannelUID(getThing().getUID(), CHANNEL_I2), newState);
                    break;
                }
                case "I3": {
                    int state = entry.getValue().getAsInt();
                    State newState = new DecimalType(state / 1000);
                    updateState(new ChannelUID(getThing().getUID(), CHANNEL_I3), newState);
                    break;
                }
                case "P": {
                    long state = entry.getValue().getAsLong();
                    State newState = new DecimalType(state / 1000);
                    updateState(new ChannelUID(getThing().getUID(), CHANNEL_POWER), newState);
                    break;
                }
                case "PF": {
                    int state = entry.getValue().getAsInt();
                    State newState = new PercentType(state / 10);
                    updateState(new ChannelUID(getThing().getUID(), CHANNEL_POWER_FACTOR), newState);
                    break;
                }
                case "E pres": {
                    long state = entry.getValue().getAsLong();
                    State newState = new DecimalType(state / 10);
                    updateState(new ChannelUID(getThing().getUID(), CHANNEL_SESSION_CONSUMPTION), newState);
                    break;
                }
                case "E total": {
                    long state = entry.getValue().getAsLong();
                    State newState = new DecimalType(state / 10);
                    updateState(new ChannelUID(getThing().getUID(), CHANNEL_TOTAL_CONSUMPTION), newState);
                    break;
                }
                }
            }

        } catch (JsonParseException e) {
            logger.debug("Invalid JSON data will be ignored: '{}'", response);
        }
    }

    @Override
    public void handleCommand(ChannelUID channelUID, Command command) {

        switch (channelUID.getId()) {
        case CHANNEL_MAX_PRESET_CURRENT: {
            if (command instanceof DecimalType) {
                sendCommand("curr " + String
                        .valueOf(Math.min(Math.max(6000, ((DecimalType) command).intValue()), maxSystemCurrent)));
            }
            break;
        }
        case CHANNEL_MAX_PRESET_CURRENT_RANGE: {
            if (command instanceof OnOffType || command instanceof IncreaseDecreaseType
                    || command instanceof PercentType) {

                int newValue = 6000;
                if (command == IncreaseDecreaseType.INCREASE) {
                    newValue = Math.min(Math.max(6000, maxPresetCurrent + 1), maxSystemCurrent);
                } else if (command == IncreaseDecreaseType.DECREASE) {
                    newValue = Math.min(Math.max(6000, maxPresetCurrent - 1), maxSystemCurrent);
                } else if (command == OnOffType.ON) {
                    newValue = maxSystemCurrent;
                } else if (command == OnOffType.OFF) {
                    newValue = 6000;
                } else if (command instanceof PercentType) {
                    newValue = 6000 + (maxSystemCurrent - 6000) * ((PercentType) command).intValue() / 100;
                } else {
                    return;
                }

                sendCommand("curr " + String.valueOf(newValue));
            }
            break;
        }
        case CHANNEL_ENABLED: {
            if (command instanceof OnOffType) {

                if (command == OnOffType.ON) {
                    sendCommand("ena 1");
                } else if (command == OnOffType.OFF) {
                    sendCommand("ena 0");
                } else {
                    return;
                }

            }
            break;
        }
        case CHANNEL_OUTPUT: {
            if (command instanceof OnOffType) {

                if (command == OnOffType.ON) {
                    sendCommand("output 1");
                } else if (command == OnOffType.OFF) {
                    sendCommand("output 0");
                } else {
                    return;
                }

            }
            break;
        }
        }
    }

    private void sendCommand(String command) {

        if (command != null) {
            ByteBuffer byteBuffer = ByteBuffer.allocate(command.getBytes().length);
            try {
                byteBuffer.put(command.getBytes("ASCII"));
                onWritable(byteBuffer, datagramChannel);
            } catch (UnsupportedEncodingException | NumberFormatException e) {
                logger.error("An exception occurred while sending a command to the KeContact wallbox for '{}': {}",
                        getThing().getUID(), e.getMessage());
            }
        }

    }

}