/**
 * Copyright (c) 2010-2020 Contributors to the openHAB project
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 */

 /* ***********************************************************
 * This file was automatically generated on 2022-01-03.      *
 *                                                           *
 * openHAB Bindings Version 2.0.0                            *
 *                                                           *
 * If you have a bugfix for this file and want to commit it, *
 * please fix the bug in the generator. You can find a link  *
 * to the generators git repository on tinkerforge.com       *
 *************************************************************/

package org.openhab.binding.tinkerforge.internal.device;

import java.net.URI;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.smarthome.config.core.Configuration;
import org.eclipse.smarthome.config.core.ConfigDescription;
import org.eclipse.smarthome.config.core.ConfigDescriptionBuilder;
import org.eclipse.smarthome.config.core.ConfigDescriptionParameter.Type;
import org.eclipse.smarthome.config.core.ConfigDescriptionParameterBuilder;
import org.eclipse.smarthome.config.core.ConfigDescriptionParameterGroup;
import org.eclipse.smarthome.config.core.ParameterOption;
import org.eclipse.smarthome.core.types.State;
import org.eclipse.smarthome.core.types.StateOption;
import org.eclipse.smarthome.core.types.Command;
import org.eclipse.smarthome.core.types.CommandDescriptionBuilder;
import org.eclipse.smarthome.core.types.CommandOption;
import org.eclipse.smarthome.core.thing.ChannelUID;
import org.eclipse.smarthome.core.thing.ThingStatus;
import org.eclipse.smarthome.core.thing.ThingTypeUID;
import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
import org.eclipse.smarthome.core.thing.type.ChannelDefinitionBuilder;
import org.eclipse.smarthome.core.thing.type.ChannelType;
import org.eclipse.smarthome.core.thing.type.ChannelTypeBuilder;
import org.eclipse.smarthome.core.thing.type.ChannelTypeUID;
import org.eclipse.smarthome.core.types.RefreshType;
import org.eclipse.smarthome.core.thing.type.ThingType;
import org.eclipse.smarthome.core.thing.type.ThingTypeBuilder;
import org.eclipse.smarthome.core.types.StateDescriptionFragmentBuilder;
import org.openhab.binding.tinkerforge.internal.TinkerforgeBindingConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.smarthome.core.library.types.DecimalType;
import org.eclipse.smarthome.core.library.types.QuantityType;
import org.eclipse.smarthome.core.library.unit.MetricPrefix;
import org.eclipse.smarthome.core.library.unit.SIUnits;
import org.eclipse.smarthome.core.library.unit.SmartHomeUnits;
import org.eclipse.smarthome.core.library.types.OnOffType;
import org.eclipse.smarthome.core.library.types.OpenClosedType;
import org.eclipse.smarthome.core.library.types.StringType;

import com.tinkerforge.BrickletIO4;
import com.tinkerforge.IPConnection;
import com.tinkerforge.TinkerforgeException;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

/**
 * Wraps the Java Bindings of the IO-4 Bricklet.
 * @author Erik Fleckstein - Initial contribution
 */
@NonNullByDefault
public class BrickletIO4Wrapper extends BrickletIO4 implements StandardFlashable, DeviceWrapper {
    public static final DeviceInfo DEVICE_INFO = new DeviceInfo(BrickletIO4.DEVICE_DISPLAY_NAME, "brickletio4", BrickletIO4.DEVICE_IDENTIFIER, BrickletIO4Wrapper.class, BrickletIO4Actions.class, "2.0.1", false);

    public BrickletIO4Wrapper(String uid, IPConnection ipcon) {
        super(uid, ipcon);
    }

    private List<ScheduledFuture<?>> manualChannelUpdates = new ArrayList<>();
    private List<ListenerReg<?>> listenerRegs = new ArrayList<>();

    public void cancelManualUpdates() {
        manualChannelUpdates.forEach(f -> f.cancel(true));
    }

    public <T> T reg(T listener, Consumer<T> toRemove) {
        listenerRegs.add(new ListenerReg<T>(listener, toRemove));
        return listener;
    }

    
    private final Logger logger = LoggerFactory.getLogger(BrickletIO4Wrapper.class);

    @Override
    public void initialize(org.eclipse.smarthome.config.core.Configuration config, Function<String, org.eclipse.smarthome.config.core.Configuration> getChannelConfigFn, BiConsumer<String, org.eclipse.smarthome.core.types.State> updateStateFn, BiConsumer<String, String> triggerChannelFn, ScheduledExecutorService scheduler, BaseThingHandler handler) throws TinkerforgeException {
        this.setResponseExpectedAll(true);
        BrickletIO4Config cfg = (BrickletIO4Config) config.as(BrickletIO4Config.class);
        if(cfg.pinConfiguration0 > 1) {
this.addInterruptListener(this.reg((interruptMask, valueMask) -> {if((interruptMask & (1 << 0)) > 0) {updateStateFn.accept("BrickletIO4Input0", transformBrickletIO4Input0Callback0(interruptMask, valueMask, cfg));}}, this::removeInterruptListener));}
		if(cfg.pinConfiguration1 > 1) {
this.addInterruptListener(this.reg((interruptMask, valueMask) -> {if((interruptMask & (1 << 1)) > 0) {updateStateFn.accept("BrickletIO4Input1", transformBrickletIO4Input1Callback0(interruptMask, valueMask, cfg));}}, this::removeInterruptListener));}
		if(cfg.pinConfiguration2 > 1) {
this.addInterruptListener(this.reg((interruptMask, valueMask) -> {if((interruptMask & (1 << 2)) > 0) {updateStateFn.accept("BrickletIO4Input2", transformBrickletIO4Input2Callback0(interruptMask, valueMask, cfg));}}, this::removeInterruptListener));}
		if(cfg.pinConfiguration3 > 1) {
this.addInterruptListener(this.reg((interruptMask, valueMask) -> {if((interruptMask & (1 << 3)) > 0) {updateStateFn.accept("BrickletIO4Input3", transformBrickletIO4Input3Callback0(interruptMask, valueMask, cfg));}}, this::removeInterruptListener));}
		if(cfg.pinConfiguration0 <= 1) {
this.addMonoflopDoneListener(this.reg((selectionMask, valueMask) -> {if((selectionMask & (1 << 0)) > 0) {updateStateFn.accept("BrickletIO4Output0", transformBrickletIO4Output0Callback0(selectionMask, valueMask, cfg));}}, this::removeMonoflopDoneListener));}
		if(cfg.pinConfiguration1 <= 1) {
this.addMonoflopDoneListener(this.reg((selectionMask, valueMask) -> {if((selectionMask & (1 << 1)) > 0) {updateStateFn.accept("BrickletIO4Output1", transformBrickletIO4Output1Callback0(selectionMask, valueMask, cfg));}}, this::removeMonoflopDoneListener));}
		if(cfg.pinConfiguration2 <= 1) {
this.addMonoflopDoneListener(this.reg((selectionMask, valueMask) -> {if((selectionMask & (1 << 2)) > 0) {updateStateFn.accept("BrickletIO4Output2", transformBrickletIO4Output2Callback0(selectionMask, valueMask, cfg));}}, this::removeMonoflopDoneListener));}
		if(cfg.pinConfiguration3 <= 1) {
this.addMonoflopDoneListener(this.reg((selectionMask, valueMask) -> {if((selectionMask & (1 << 3)) > 0) {updateStateFn.accept("BrickletIO4Output3", transformBrickletIO4Output3Callback0(selectionMask, valueMask, cfg));}}, this::removeMonoflopDoneListener));}
        
		if (cfg.pinConfiguration0 > 1) {
		this.setInterrupt((short)(this.getInterrupt() | (1 << 0)));
		            this.setConfiguration((short)(1 << 0), 'i', cfg.pinConfiguration0 % 2 == 1);
		}
		if (cfg.pinConfiguration1 > 1) {
		this.setInterrupt((short)(this.getInterrupt() | (1 << 1)));
		            this.setConfiguration((short)(1 << 1), 'i', cfg.pinConfiguration1 % 2 == 1);
		}
		if (cfg.pinConfiguration2 > 1) {
		this.setInterrupt((short)(this.getInterrupt() | (1 << 2)));
		            this.setConfiguration((short)(1 << 2), 'i', cfg.pinConfiguration2 % 2 == 1);
		}
		if (cfg.pinConfiguration3 > 1) {
		this.setInterrupt((short)(this.getInterrupt() | (1 << 3)));
		            this.setConfiguration((short)(1 << 3), 'i', cfg.pinConfiguration3 % 2 == 1);
		}
		if (cfg.pinConfiguration0 <= 1) {
		this.setConfiguration((short)(1 << 0), 'o', cfg.pinConfiguration0 % 2 == 1);
		}
		if (cfg.pinConfiguration1 <= 1) {
		this.setConfiguration((short)(1 << 1), 'o', cfg.pinConfiguration1 % 2 == 1);
		}
		if (cfg.pinConfiguration2 <= 1) {
		this.setConfiguration((short)(1 << 2), 'o', cfg.pinConfiguration2 % 2 == 1);
		}
		if (cfg.pinConfiguration3 <= 1) {
		this.setConfiguration((short)(1 << 3), 'o', cfg.pinConfiguration3 % 2 == 1);
		}
		if (cfg.pinConfiguration0 > 1) {
		BrickletIO4EdgeCountConfig channelCfg = getChannelConfigFn.apply("BrickletIO4EdgeCount0").as(BrickletIO4EdgeCountConfig.class);
		this.setEdgeCountConfig((short)(1 << 0), channelCfg.edgeType.shortValue(), channelCfg.debounce.shortValue());
		if(channelCfg.updateInterval > 0) {
    this.manualChannelUpdates.add(scheduler.scheduleWithFixedDelay(() -> {if(handler.getThing().getStatus() == ThingStatus.ONLINE) {handler.handleCommand(new ChannelUID(handler.getThing().getUID(), "BrickletIO4EdgeCount0"), RefreshType.REFRESH);}}, channelCfg.updateInterval, channelCfg.updateInterval, TimeUnit.MILLISECONDS));
}
		}
		if (cfg.pinConfiguration1 > 1) {
		BrickletIO4EdgeCountConfig channelCfg = getChannelConfigFn.apply("BrickletIO4EdgeCount1").as(BrickletIO4EdgeCountConfig.class);
		this.setEdgeCountConfig((short)(1 << 1), channelCfg.edgeType.shortValue(), channelCfg.debounce.shortValue());
		if(channelCfg.updateInterval > 0) {
    this.manualChannelUpdates.add(scheduler.scheduleWithFixedDelay(() -> {if(handler.getThing().getStatus() == ThingStatus.ONLINE) {handler.handleCommand(new ChannelUID(handler.getThing().getUID(), "BrickletIO4EdgeCount1"), RefreshType.REFRESH);}}, channelCfg.updateInterval, channelCfg.updateInterval, TimeUnit.MILLISECONDS));
}
		}
		if (cfg.pinConfiguration2 > 1) {
		BrickletIO4EdgeCountConfig channelCfg = getChannelConfigFn.apply("BrickletIO4EdgeCount2").as(BrickletIO4EdgeCountConfig.class);
		this.setEdgeCountConfig((short)(1 << 2), channelCfg.edgeType.shortValue(), channelCfg.debounce.shortValue());
		if(channelCfg.updateInterval > 0) {
    this.manualChannelUpdates.add(scheduler.scheduleWithFixedDelay(() -> {if(handler.getThing().getStatus() == ThingStatus.ONLINE) {handler.handleCommand(new ChannelUID(handler.getThing().getUID(), "BrickletIO4EdgeCount2"), RefreshType.REFRESH);}}, channelCfg.updateInterval, channelCfg.updateInterval, TimeUnit.MILLISECONDS));
}
		}
		if (cfg.pinConfiguration3 > 1) {
		BrickletIO4EdgeCountConfig channelCfg = getChannelConfigFn.apply("BrickletIO4EdgeCount3").as(BrickletIO4EdgeCountConfig.class);
		this.setEdgeCountConfig((short)(1 << 3), channelCfg.edgeType.shortValue(), channelCfg.debounce.shortValue());
		if(channelCfg.updateInterval > 0) {
    this.manualChannelUpdates.add(scheduler.scheduleWithFixedDelay(() -> {if(handler.getThing().getStatus() == ThingStatus.ONLINE) {handler.handleCommand(new ChannelUID(handler.getThing().getUID(), "BrickletIO4EdgeCount3"), RefreshType.REFRESH);}}, channelCfg.updateInterval, channelCfg.updateInterval, TimeUnit.MILLISECONDS));
}
		}
    }

    @Override
    public void dispose(org.eclipse.smarthome.config.core.Configuration config) throws TinkerforgeException {
        listenerRegs.forEach(ListenerReg::deregister);

        BrickletIO4Config cfg = (BrickletIO4Config) config.as(BrickletIO4Config.class);
        if(cfg.pinConfiguration0 > 1) {
this.setInterrupt((short)(this.getInterrupt() & ~(1 << 0)));}
		if(cfg.pinConfiguration1 > 1) {
this.setInterrupt((short)(this.getInterrupt() & ~(1 << 1)));}
		if(cfg.pinConfiguration2 > 1) {
this.setInterrupt((short)(this.getInterrupt() & ~(1 << 2)));}
		if(cfg.pinConfiguration3 > 1) {
this.setInterrupt((short)(this.getInterrupt() & ~(1 << 3)));}
		
    }

    @Override
    public List<String> getEnabledChannels(org.eclipse.smarthome.config.core.Configuration config) throws TinkerforgeException{
        BrickletIO4Config cfg = (BrickletIO4Config) config.as(BrickletIO4Config.class);
List<String> result = new ArrayList<String>();
        if (cfg.pinConfiguration0 > 1) {
                result.add("BrickletIO4Input0");
            }
		if (cfg.pinConfiguration1 > 1) {
                result.add("BrickletIO4Input1");
            }
		if (cfg.pinConfiguration2 > 1) {
                result.add("BrickletIO4Input2");
            }
		if (cfg.pinConfiguration3 > 1) {
                result.add("BrickletIO4Input3");
            }
		if (cfg.pinConfiguration0 <= 1) {
                result.add("BrickletIO4Output0");
            }
		if (cfg.pinConfiguration1 <= 1) {
                result.add("BrickletIO4Output1");
            }
		if (cfg.pinConfiguration2 <= 1) {
                result.add("BrickletIO4Output2");
            }
		if (cfg.pinConfiguration3 <= 1) {
                result.add("BrickletIO4Output3");
            }
		if (cfg.pinConfiguration0 <= 1) {
                result.add("BrickletIO4Monoflop0");
            }
		if (cfg.pinConfiguration1 <= 1) {
                result.add("BrickletIO4Monoflop1");
            }
		if (cfg.pinConfiguration2 <= 1) {
                result.add("BrickletIO4Monoflop2");
            }
		if (cfg.pinConfiguration3 <= 1) {
                result.add("BrickletIO4Monoflop3");
            }
		if (cfg.pinConfiguration0 > 1) {
                result.add("BrickletIO4EdgeCount0");
            }
		if (cfg.pinConfiguration1 > 1) {
                result.add("BrickletIO4EdgeCount1");
            }
		if (cfg.pinConfiguration2 > 1) {
                result.add("BrickletIO4EdgeCount2");
            }
		if (cfg.pinConfiguration3 > 1) {
                result.add("BrickletIO4EdgeCount3");
            }
        return result;
    }

    public static @Nullable ChannelType getChannelType(ChannelTypeUID channelTypeUID) {
        switch(channelTypeUID.getId()) {
            case "BrickletIO4Input":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletIO4Input"), "NOT USED", "Contact").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletIO4Input")).withDescription("The logic level that is currently measured on the pin.").build();
            case "BrickletIO4Output":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletIO4Output"), "NOT USED", "Switch").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletIO4Output")).withDescription("The logic level that is currently set on the pin.").build();
            case "BrickletIO4Monoflop":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletIO4Monoflop"), "NOT USED", "String").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletIO4Monoflop")).withDescription("Triggers a monoflop as configured.")
.withCommandDescription(CommandDescriptionBuilder.create().withCommandOption(new CommandOption("TRIGGER", "Trigger")).build()).build();
            case "BrickletIO4EdgeCount":
                return ChannelTypeBuilder.state(new ChannelTypeUID("tinkerforge", "BrickletIO4EdgeCount"), "NOT USED", "Number:Dimensionless").withConfigDescriptionURI(URI.create("channel-type:tinkerforge:BrickletIO4EdgeCount")).withDescription("The current value of the edge counter of the pin.")
.withStateDescription(StateDescriptionFragmentBuilder.create().withPattern("%d").withReadOnly(true).build().toStateDescription()).build();
default:
                break;
        }

        return null;
    }

    public static ThingType getThingType(ThingTypeUID thingTypeUID) {
             Map<String, String> thingTypeProperties = ThingTypeBuilder.instance(thingTypeUID, "unused").build().getProperties();
             Map<String, String> props = new HashMap<String, String>(thingTypeProperties);
             props.putIfAbsent(TinkerforgeBindingConstants.PROPERTY_MINIMUM_FIRMWARE_VERSION, DEVICE_INFO.minimumFWVersion);
        return ThingTypeBuilder.instance(thingTypeUID, "Tinkerforge IO-4 Bricklet").isListed(false).withSupportedBridgeTypeUIDs(Arrays.asList(TinkerforgeBindingConstants.THING_TYPE_BRICK_DAEMON.toString())).withConfigDescriptionURI(URI.create("thing-type:tinkerforge:" + thingTypeUID.getId())).withDescription("4-channel digital input/output").withChannelDefinitions(Arrays.asList(new ChannelDefinitionBuilder("BrickletIO4Input0", new ChannelTypeUID("tinkerforge", "BrickletIO4Input")).withLabel("Input Value 0").build(), new ChannelDefinitionBuilder("BrickletIO4Input1", new ChannelTypeUID("tinkerforge", "BrickletIO4Input")).withLabel("Input Value 1").build(), new ChannelDefinitionBuilder("BrickletIO4Input2", new ChannelTypeUID("tinkerforge", "BrickletIO4Input")).withLabel("Input Value 2").build(), new ChannelDefinitionBuilder("BrickletIO4Input3", new ChannelTypeUID("tinkerforge", "BrickletIO4Input")).withLabel("Input Value 3").build(), new ChannelDefinitionBuilder("BrickletIO4Output0", new ChannelTypeUID("tinkerforge", "BrickletIO4Output")).withLabel("Output Value 0").build(), new ChannelDefinitionBuilder("BrickletIO4Output1", new ChannelTypeUID("tinkerforge", "BrickletIO4Output")).withLabel("Output Value 1").build(), new ChannelDefinitionBuilder("BrickletIO4Output2", new ChannelTypeUID("tinkerforge", "BrickletIO4Output")).withLabel("Output Value 2").build(), new ChannelDefinitionBuilder("BrickletIO4Output3", new ChannelTypeUID("tinkerforge", "BrickletIO4Output")).withLabel("Output Value 3").build(), new ChannelDefinitionBuilder("BrickletIO4Monoflop0", new ChannelTypeUID("tinkerforge", "BrickletIO4Monoflop")).withLabel("Monoflop 0").build(), new ChannelDefinitionBuilder("BrickletIO4Monoflop1", new ChannelTypeUID("tinkerforge", "BrickletIO4Monoflop")).withLabel("Monoflop 1").build(), new ChannelDefinitionBuilder("BrickletIO4Monoflop2", new ChannelTypeUID("tinkerforge", "BrickletIO4Monoflop")).withLabel("Monoflop 2").build(), new ChannelDefinitionBuilder("BrickletIO4Monoflop3", new ChannelTypeUID("tinkerforge", "BrickletIO4Monoflop")).withLabel("Monoflop 3").build(), new ChannelDefinitionBuilder("BrickletIO4EdgeCount0", new ChannelTypeUID("tinkerforge", "BrickletIO4EdgeCount")).withLabel("Edge Count 0").build(), new ChannelDefinitionBuilder("BrickletIO4EdgeCount1", new ChannelTypeUID("tinkerforge", "BrickletIO4EdgeCount")).withLabel("Edge Count 1").build(), new ChannelDefinitionBuilder("BrickletIO4EdgeCount2", new ChannelTypeUID("tinkerforge", "BrickletIO4EdgeCount")).withLabel("Edge Count 2").build(), new ChannelDefinitionBuilder("BrickletIO4EdgeCount3", new ChannelTypeUID("tinkerforge", "BrickletIO4EdgeCount")).withLabel("Edge Count 3").build())).withProperties(props).build();
    }

    public static @Nullable ConfigDescription getConfigDescription(URI uri) {
        switch(uri.toASCIIString()) {
            case "thing-type:tinkerforge:brickletio4":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList(ConfigDescriptionParameterBuilder.create("pinConfiguration0", Type.INTEGER).withDefault("3").withDescription("Configures pin 0 as input or output. Inputs without pull-up will be floating if nothing is connected. Outputs can have an initial state of low or high.").withLabel("Pin Configuration 0").withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("3", "Input with pull-up"), new ParameterOption("2", "Input without pull-up"), new ParameterOption("1", "Output (Initial high)"), new ParameterOption("0", "Output (Initial low)"))).build(), ConfigDescriptionParameterBuilder.create("pinConfiguration1", Type.INTEGER).withDefault("3").withDescription("Configures pin 1 as input or output. Inputs without pull-up will be floating if nothing is connected. Outputs can have an initial state of low or high.").withLabel("Pin Configuration 1").withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("3", "Input with pull-up"), new ParameterOption("2", "Input without pull-up"), new ParameterOption("1", "Output (Initial high)"), new ParameterOption("0", "Output (Initial low)"))).build(), ConfigDescriptionParameterBuilder.create("pinConfiguration2", Type.INTEGER).withDefault("3").withDescription("Configures pin 2 as input or output. Inputs without pull-up will be floating if nothing is connected. Outputs can have an initial state of low or high.").withLabel("Pin Configuration 2").withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("3", "Input with pull-up"), new ParameterOption("2", "Input without pull-up"), new ParameterOption("1", "Output (Initial high)"), new ParameterOption("0", "Output (Initial low)"))).build(), ConfigDescriptionParameterBuilder.create("pinConfiguration3", Type.INTEGER).withDefault("3").withDescription("Configures pin 3 as input or output. Inputs without pull-up will be floating if nothing is connected. Outputs can have an initial state of low or high.").withLabel("Pin Configuration 3").withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("3", "Input with pull-up"), new ParameterOption("2", "Input without pull-up"), new ParameterOption("1", "Output (Initial high)"), new ParameterOption("0", "Output (Initial low)"))).build())).withParameterGroups(Arrays.asList()).build();
            case "channel-type:tinkerforge:BrickletIO4Input":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList()).withParameterGroups(Arrays.asList()).build();
            case "channel-type:tinkerforge:BrickletIO4Output":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList()).withParameterGroups(Arrays.asList()).build();
            case "channel-type:tinkerforge:BrickletIO4Monoflop":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList(ConfigDescriptionParameterBuilder.create("monoflopDuration", Type.INTEGER).withDefault("1000").withDescription("The time that the pin should hold the configured value.").withLabel("Monoflop Duration").withUnit("ms").withAdvanced(true).withMinimum(BigDecimal.valueOf(0)).withMaximum(BigDecimal.valueOf(4294967295L)).build(), ConfigDescriptionParameterBuilder.create("monoflopValue", Type.BOOLEAN).withDefault("true").withDescription("The desired value of the pin.").withLabel("Monoflop Value").withAdvanced(true).build())).withParameterGroups(Arrays.asList()).build();
            case "channel-type:tinkerforge:BrickletIO4EdgeCount":
                return ConfigDescriptionBuilder.create(uri).withParameters(Arrays.asList(ConfigDescriptionParameterBuilder.create("edgeType", Type.INTEGER).withDefault("0").withDescription("Configures if rising edges, falling edges or both are counted.").withLabel("Edge Type").withAdvanced(true).withLimitToOptions(true).withOptions(Arrays.asList(new ParameterOption("0", "Rising"), new ParameterOption("1", "Falling"), new ParameterOption("2", "Both"))).build(), ConfigDescriptionParameterBuilder.create("debounce", Type.INTEGER).withDefault("100").withDescription("The debounce time is the minimum time between two count increments.").withLabel("Debounce Time").withMinimum(BigDecimal.valueOf(0)).withMaximum(BigDecimal.valueOf(4294967295L)).build(), ConfigDescriptionParameterBuilder.create("resetOnRead", Type.BOOLEAN).withDefault("false").withDescription("Enabling this will reset the edge counter after openHAB reads its value. Use this if you want relative counts per update.").withLabel("Reset Edge Counter On Update").build(), ConfigDescriptionParameterBuilder.create("updateInterval", Type.INTEGER).withDefault("1000").withDescription("Specifies the update interval in milliseconds. A value of 0 disables automatic updates.").withLabel("Update Interval").withUnit("ms").build())).withParameterGroups(Arrays.asList()).build();
            default:
                break;
        }
        return null;
    }

        @Override
    public void refreshValue(String channel, org.eclipse.smarthome.config.core.Configuration config, org.eclipse.smarthome.config.core.Configuration channelConfig, BiConsumer<String, org.eclipse.smarthome.core.types.State> updateStateFn, BiConsumer<String, String> triggerChannelFn) throws TinkerforgeException {
        BrickletIO4Config cfg = (BrickletIO4Config) config.as(BrickletIO4Config.class);
switch(channel) {
            case "BrickletIO4Input0":
                   {
            short value = this.getValue();
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4Input0Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4Input1":
                   {
            short value = this.getValue();
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4Input1Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4Input2":
                   {
            short value = this.getValue();
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4Input2Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4Input3":
                   {
            short value = this.getValue();
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4Input3Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4Output0":
                   {
            short value = this.getValue();
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4Output0Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4Output1":
                   {
            short value = this.getValue();
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4Output1Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4Output2":
                   {
            short value = this.getValue();
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4Output2Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4Output3":
                   {
            short value = this.getValue();
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4Output3Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4Monoflop0":
                   {
            BrickletIO4.Monoflop value = this.getMonoflop((short)0);
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4Monoflop0Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4Monoflop1":
                   {
            BrickletIO4.Monoflop value = this.getMonoflop((short)1);
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4Monoflop1Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4Monoflop2":
                   {
            BrickletIO4.Monoflop value = this.getMonoflop((short)2);
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4Monoflop2Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4Monoflop3":
                   {
            BrickletIO4.Monoflop value = this.getMonoflop((short)3);
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4Monoflop3Getter0(value, cfg));
        }
                   return;
            case "BrickletIO4EdgeCount0": {
                   BrickletIO4EdgeCountConfig channelCfg = channelConfig.as(BrickletIO4EdgeCountConfig.class);
                   {
            long value = this.getEdgeCount((short)0, channelCfg.resetOnRead);
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4EdgeCount0Getter0(value, cfg));
        }
                   return;
               }
            case "BrickletIO4EdgeCount1": {
                   BrickletIO4EdgeCountConfig channelCfg = channelConfig.as(BrickletIO4EdgeCountConfig.class);
                   {
            long value = this.getEdgeCount((short)1, channelCfg.resetOnRead);
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4EdgeCount1Getter0(value, cfg));
        }
                   return;
               }
            case "BrickletIO4EdgeCount2": {
                   BrickletIO4EdgeCountConfig channelCfg = channelConfig.as(BrickletIO4EdgeCountConfig.class);
                   {
            long value = this.getEdgeCount((short)2, channelCfg.resetOnRead);
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4EdgeCount2Getter0(value, cfg));
        }
                   return;
               }
            case "BrickletIO4EdgeCount3": {
                   BrickletIO4EdgeCountConfig channelCfg = channelConfig.as(BrickletIO4EdgeCountConfig.class);
                   {
            long value = this.getEdgeCount((short)3, channelCfg.resetOnRead);
            if(true)
                updateStateFn.accept(channel, transformBrickletIO4EdgeCount3Getter0(value, cfg));
        }
                   return;
               }
default:
                logger.warn("Refresh for unknown channel {}", channel);
                break;
        }
    }
    

        @Override
    public List<SetterRefresh> handleCommand(org.eclipse.smarthome.config.core.Configuration config, org.eclipse.smarthome.config.core.Configuration channelConfig, String channel, Command command) throws TinkerforgeException {
        List<SetterRefresh> result = new ArrayList<SetterRefresh>();
        
        switch(channel) {
            case "BrickletIO4Output0":
                if (command instanceof OnOffType) {
                    OnOffType cmd = (OnOffType) command;
this.setSelectedValues((short)(1 << 0), cmd == OnOffType.ON ? (short)0xFF : (short)0);
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of OnOffType.", command.getClass().getName(), channel);
                }
                
                break;
            case "BrickletIO4Output1":
                if (command instanceof OnOffType) {
                    OnOffType cmd = (OnOffType) command;
this.setSelectedValues((short)(1 << 1), cmd == OnOffType.ON ? (short)0xFF : (short)0);
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of OnOffType.", command.getClass().getName(), channel);
                }
                
                break;
            case "BrickletIO4Output2":
                if (command instanceof OnOffType) {
                    OnOffType cmd = (OnOffType) command;
this.setSelectedValues((short)(1 << 2), cmd == OnOffType.ON ? (short)0xFF : (short)0);
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of OnOffType.", command.getClass().getName(), channel);
                }
                
                break;
            case "BrickletIO4Output3":
                if (command instanceof OnOffType) {
                    OnOffType cmd = (OnOffType) command;
this.setSelectedValues((short)(1 << 3), cmd == OnOffType.ON ? (short)0xFF : (short)0);
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of OnOffType.", command.getClass().getName(), channel);
                }
                
                break;
            case "BrickletIO4Monoflop0":
                if (command instanceof StringType) {
                    BrickletIO4MonoflopConfig channelCfg = channelConfig.as(BrickletIO4MonoflopConfig.class);
this.setMonoflop((short)(1 << 0), channelCfg.monoflopValue.booleanValue() ? (short)0xFF : (short)0, channelCfg.monoflopDuration.longValue());
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of StringType.", command.getClass().getName(), channel);
                }
                result.add(new SetterRefresh("BrickletIO4Output0", 0));
                break;
            case "BrickletIO4Monoflop1":
                if (command instanceof StringType) {
                    BrickletIO4MonoflopConfig channelCfg = channelConfig.as(BrickletIO4MonoflopConfig.class);
this.setMonoflop((short)(1 << 1), channelCfg.monoflopValue.booleanValue() ? (short)0xFF : (short)0, channelCfg.monoflopDuration.longValue());
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of StringType.", command.getClass().getName(), channel);
                }
                result.add(new SetterRefresh("BrickletIO4Output1", 0));
                break;
            case "BrickletIO4Monoflop2":
                if (command instanceof StringType) {
                    BrickletIO4MonoflopConfig channelCfg = channelConfig.as(BrickletIO4MonoflopConfig.class);
this.setMonoflop((short)(1 << 2), channelCfg.monoflopValue.booleanValue() ? (short)0xFF : (short)0, channelCfg.monoflopDuration.longValue());
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of StringType.", command.getClass().getName(), channel);
                }
                result.add(new SetterRefresh("BrickletIO4Output2", 0));
                break;
            case "BrickletIO4Monoflop3":
                if (command instanceof StringType) {
                    BrickletIO4MonoflopConfig channelCfg = channelConfig.as(BrickletIO4MonoflopConfig.class);
this.setMonoflop((short)(1 << 3), channelCfg.monoflopValue.booleanValue() ? (short)0xFF : (short)0, channelCfg.monoflopDuration.longValue());
                }
        
                else {
                    logger.warn("Command type {} not supported for channel {}. Please use one of StringType.", command.getClass().getName(), channel);
                }
                result.add(new SetterRefresh("BrickletIO4Output3", 0));
                break;
default:
                logger.warn("Command for unknown channel {}", channel);
        }
        return result;
    }

        private org.eclipse.smarthome.core.types.State transformBrickletIO4Input0Callback0(short interruptMask, short valueMask, BrickletIO4Config cfg) {
        return (valueMask & (1 << 0)) > 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Input1Callback0(short interruptMask, short valueMask, BrickletIO4Config cfg) {
        return (valueMask & (1 << 1)) > 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Input2Callback0(short interruptMask, short valueMask, BrickletIO4Config cfg) {
        return (valueMask & (1 << 2)) > 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Input3Callback0(short interruptMask, short valueMask, BrickletIO4Config cfg) {
        return (valueMask & (1 << 3)) > 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Output0Callback0(short selectionMask, short valueMask, BrickletIO4Config cfg) {
        return (valueMask & (1 << 0)) > 0 ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Output1Callback0(short selectionMask, short valueMask, BrickletIO4Config cfg) {
        return (valueMask & (1 << 1)) > 0 ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Output2Callback0(short selectionMask, short valueMask, BrickletIO4Config cfg) {
        return (valueMask & (1 << 2)) > 0 ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Output3Callback0(short selectionMask, short valueMask, BrickletIO4Config cfg) {
        return (valueMask & (1 << 3)) > 0 ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Input0Getter0(short value, BrickletIO4Config cfg) {
        return (value & (1 << 0)) > 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Input1Getter0(short value, BrickletIO4Config cfg) {
        return (value & (1 << 1)) > 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Input2Getter0(short value, BrickletIO4Config cfg) {
        return (value & (1 << 2)) > 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Input3Getter0(short value, BrickletIO4Config cfg) {
        return (value & (1 << 3)) > 0 ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Output0Getter0(short value, BrickletIO4Config cfg) {
        return (value & (1 << 0)) > 0 ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Output1Getter0(short value, BrickletIO4Config cfg) {
        return (value & (1 << 1)) > 0 ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Output2Getter0(short value, BrickletIO4Config cfg) {
        return (value & (1 << 2)) > 0 ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Output3Getter0(short value, BrickletIO4Config cfg) {
        return (value & (1 << 3)) > 0 ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Monoflop0Getter0(BrickletIO4.Monoflop value, BrickletIO4Config cfg) {
        return value.value > 0 ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Monoflop1Getter0(BrickletIO4.Monoflop value, BrickletIO4Config cfg) {
        return value.value > 0 ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Monoflop2Getter0(BrickletIO4.Monoflop value, BrickletIO4Config cfg) {
        return value.value > 0 ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4Monoflop3Getter0(BrickletIO4.Monoflop value, BrickletIO4Config cfg) {
        return value.value > 0 ? OnOffType.ON : OnOffType.OFF;
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4EdgeCount0Getter0(long value, BrickletIO4Config cfg) {
        return new DecimalType(value);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4EdgeCount1Getter0(long value, BrickletIO4Config cfg) {
        return new DecimalType(value);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4EdgeCount2Getter0(long value, BrickletIO4Config cfg) {
        return new DecimalType(value);
    }
	    private org.eclipse.smarthome.core.types.State transformBrickletIO4EdgeCount3Getter0(long value, BrickletIO4Config cfg) {
        return new DecimalType(value);
    }

}