<commit_before>/**
 * 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
 */
package org.openhab.binding.lcn.internal;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.lcn.internal.common.DimmerOutputCommand;
import org.openhab.binding.lcn.internal.common.LcnAddr;
import org.openhab.binding.lcn.internal.common.LcnAddrMod;
import org.openhab.binding.lcn.internal.common.LcnChannelGroup;
import org.openhab.binding.lcn.internal.common.LcnException;
import org.openhab.binding.lcn.internal.connection.Connection;
import org.openhab.binding.lcn.internal.connection.ModInfo;
import org.openhab.binding.lcn.internal.converter.Converter;
import org.openhab.binding.lcn.internal.converter.Converters;
import org.openhab.binding.lcn.internal.converter.InversionConverter;
import org.openhab.binding.lcn.internal.converter.S0Converter;
import org.openhab.binding.lcn.internal.subhandler.AbstractLcnModuleSubHandler;
import org.openhab.binding.lcn.internal.subhandler.LcnModuleMetaAckSubHandler;
import org.openhab.binding.lcn.internal.subhandler.LcnModuleMetaFirmwareSubHandler;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StopMoveType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.types.UpDownType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The {@link LcnModuleHandler} is responsible for handling commands, which are
 * sent to or received from one of the channels.
 *
 * @author <NAME> - Initial contribution
 */
@NonNullByDefault
public class LcnModuleHandler extends BaseThingHandler {
    private final Logger logger = LoggerFactory.getLogger(LcnModuleHandler.class);
    private static final Map<String, Converter> VALUE_CONVERTERS = new HashMap<>();
    private static final InversionConverter INVERSION_CONVERTER = new InversionConverter();
    private @Nullable LcnAddrMod moduleAddress;
    private final Map<LcnChannelGroup, AbstractLcnModuleSubHandler> subHandlers = new HashMap<>();
    private final List<AbstractLcnModuleSubHandler> metadataSubHandlers = new ArrayList<>();
    private final Map<ChannelUID, Converter> converters = new HashMap<>();

    static {
        VALUE_CONVERTERS.put("temperature", Converters.TEMPERATURE);
        VALUE_CONVERTERS.put("light", Converters.LIGHT);
        VALUE_CONVERTERS.put("co2", Converters.CO2);
        VALUE_CONVERTERS.put("current", Converters.CURRENT);
        VALUE_CONVERTERS.put("voltage", Converters.VOLTAGE);
        VALUE_CONVERTERS.put("angle", Converters.ANGLE);
        VALUE_CONVERTERS.put("windspeed", Converters.WINDSPEED);
    }

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

    @Override
    public void initialize() {
        LcnModuleConfiguration localConfig = getConfigAs(LcnModuleConfiguration.class);
        LcnAddrMod localModuleAddress = moduleAddress = new LcnAddrMod(localConfig.segmentId, localConfig.moduleId);

        try {
            // Determine serial number of manually added modules
            requestFirmwareVersionAndSerialNumberIfNotSet();

            // create sub handlers
            ModInfo info = getPckGatewayHandler().getModInfo(localModuleAddress);
            for (LcnChannelGroup type : LcnChannelGroup.values()) {
                subHandlers.put(type, type.createSubHandler(this, info));
            }

            // meta sub handlers, which are not assigned to a channel group
            metadataSubHandlers.add(new LcnModuleMetaAckSubHandler(this, info));
            metadataSubHandlers.add(new LcnModuleMetaFirmwareSubHandler(this, info));

            // initialize converters
            for (Channel channel : thing.getChannels()) {
                Object unitObject = channel.getConfiguration().get("unit");
                Object parameterObject = channel.getConfiguration().get("parameter");
                Object invertState = channel.getConfiguration().get("invertState");
                Object invertUpDown = channel.getConfiguration().get("invertUpDown");

                // Initialize value converters
                if (unitObject instanceof String) {
                    switch ((String) unitObject) {
                        case "power":
                        case "energy":
                            converters.put(channel.getUID(), new S0Converter(parameterObject));
                            break;
                        default:
                            Converter converter = VALUE_CONVERTERS.get(unitObject);
                            if (converter != null) {
                                converters.put(channel.getUID(), converter);
                            }
                            break;
                    }
                }

                // Initialize inversion converter
                if (Boolean.TRUE.equals(invertState) || Boolean.TRUE.equals(invertUpDown)) {
                    converters.put(channel.getUID(), INVERSION_CONVERTER);
                }

            }

            // module is assumed as online, when the corresponding Bridge (PckGatewayHandler) is online.
            updateStatus(ThingStatus.ONLINE);
        } catch (LcnException e) {
            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
        }
    }

    /**
     * Triggers requesting the firmware version of the LCN module. The message also contains the serial number.
     *
     * @throws LcnException when the handler is not initialized
     */
    @SuppressWarnings("null")
    protected void requestFirmwareVersionAndSerialNumberIfNotSet() throws LcnException {
        String serialNumber = getThing().getProperties().get(Thing.PROPERTY_SERIAL_NUMBER);
        if (serialNumber == null || serialNumber.isEmpty()) {
            LcnAddrMod localModuleAddress = moduleAddress;
            if (localModuleAddress != null) {
                getPckGatewayHandler().getModInfo(localModuleAddress).requestFirmwareVersion();
            }
        }
    }

    @Override
    public void handleCommand(ChannelUID channelUid, Command command) {
        try {
            String groupId = channelUid.getGroupId();

            if (!channelUid.isInGroup()) {
                return;
            }<commit_msg>[lcn] Fix initial polling of linked Channels (#9103)<commit_after>/**
 * 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
 */
package org.openhab.binding.lcn.internal;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.lcn.internal.common.DimmerOutputCommand;
import org.openhab.binding.lcn.internal.common.LcnAddr;
import org.openhab.binding.lcn.internal.common.LcnAddrMod;
import org.openhab.binding.lcn.internal.common.LcnChannelGroup;
import org.openhab.binding.lcn.internal.common.LcnException;
import org.openhab.binding.lcn.internal.connection.Connection;
import org.openhab.binding.lcn.internal.connection.ModInfo;
import org.openhab.binding.lcn.internal.converter.Converter;
import org.openhab.binding.lcn.internal.converter.Converters;
import org.openhab.binding.lcn.internal.converter.InversionConverter;
import org.openhab.binding.lcn.internal.converter.S0Converter;
import org.openhab.binding.lcn.internal.subhandler.AbstractLcnModuleSubHandler;
import org.openhab.binding.lcn.internal.subhandler.LcnModuleMetaAckSubHandler;
import org.openhab.binding.lcn.internal.subhandler.LcnModuleMetaFirmwareSubHandler;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StopMoveType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.types.UpDownType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The {@link LcnModuleHandler} is responsible for handling commands, which are
 * sent to or received from one of the channels.
 *
 * @author <NAME> - Initial contribution
 */
@NonNullByDefault
public class LcnModuleHandler extends BaseThingHandler {
    private final Logger logger = LoggerFactory.getLogger(LcnModuleHandler.class);
    private static final Map<String, Converter> VALUE_CONVERTERS = new HashMap<>();
    private static final InversionConverter INVERSION_CONVERTER = new InversionConverter();
    private @Nullable LcnAddrMod moduleAddress;
    private final Map<LcnChannelGroup, AbstractLcnModuleSubHandler> subHandlers = new HashMap<>();
    private final List<AbstractLcnModuleSubHandler> metadataSubHandlers = new ArrayList<>();
    private final Map<ChannelUID, Converter> converters = new HashMap<>();

    static {
        VALUE_CONVERTERS.put("temperature", Converters.TEMPERATURE);
        VALUE_CONVERTERS.put("light", Converters.LIGHT);
        VALUE_CONVERTERS.put("co2", Converters.CO2);
        VALUE_CONVERTERS.put("current", Converters.CURRENT);
        VALUE_CONVERTERS.put("voltage", Converters.VOLTAGE);
        VALUE_CONVERTERS.put("angle", Converters.ANGLE);
        VALUE_CONVERTERS.put("windspeed", Converters.WINDSPEED);
    }

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

    @Override
    public void initialize() {
        LcnModuleConfiguration localConfig = getConfigAs(LcnModuleConfiguration.class);
        LcnAddrMod localModuleAddress = moduleAddress = new LcnAddrMod(localConfig.segmentId, localConfig.moduleId);

        try {
            // Determine serial number of manually added modules
            requestFirmwareVersionAndSerialNumberIfNotSet();

            // create sub handlers
            ModInfo info = getPckGatewayHandler().getModInfo(localModuleAddress);
            for (LcnChannelGroup type : LcnChannelGroup.values()) {
                subHandlers.put(type, type.createSubHandler(this, info));
            }

            // meta sub handlers, which are not assigned to a channel group
            metadataSubHandlers.add(new LcnModuleMetaAckSubHandler(this, info));
            metadataSubHandlers.add(new LcnModuleMetaFirmwareSubHandler(this, info));

            // initialize converters
            for (Channel channel : thing.getChannels()) {
                Object unitObject = channel.getConfiguration().get("unit");
                Object parameterObject = channel.getConfiguration().get("parameter");
                Object invertState = channel.getConfiguration().get("invertState");
                Object invertUpDown = channel.getConfiguration().get("invertUpDown");

                // Initialize value converters
                if (unitObject instanceof String) {
                    switch ((String) unitObject) {
                        case "power":
                        case "energy":
                            converters.put(channel.getUID(), new S0Converter(parameterObject));
                            break;
                        default:
                            Converter converter = VALUE_CONVERTERS.get(unitObject);
                            if (converter != null) {
                                converters.put(channel.getUID(), converter);
                            }
                            break;
                    }
                }

                // Initialize inversion converter
                if (Boolean.TRUE.equals(invertState) || Boolean.TRUE.equals(invertUpDown)) {
                    converters.put(channel.getUID(), INVERSION_CONVERTER);
                }

            }

            // module is assumed as online, when the corresponding Bridge (PckGatewayHandler) is online.
            updateStatus(ThingStatus.ONLINE);

            // trigger REFRESH commands for all linked Channels to start polling
            getThing().getChannels().forEach(channel -> {
                if (isLinked(channel.getUID())) {
                    channelLinked(channel.getUID());
                }
            });
        } catch (LcnException e) {
            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
        }
    }

    /**
     * Triggers requesting the firmware version of the LCN module. The message also contains the serial number.
     *
     * @throws LcnException when the handler is not initialized
     */
    @SuppressWarnings("null")
    protected void requestFirmwareVersionAndSerialNumberIfNotSet() throws LcnException {
        String serialNumber = getThing().getProperties().get(Thing.PROPERTY_SERIAL_NUMBER);
        if (serialNumber == null || serialNumber.isEmpty()) {
            LcnAddrMod localModuleAddress = moduleAddress;
            if (localModuleAddress != null) {
                getPckGatewayHandler().getModInfo(localModuleAddress).requestFirmwareVersion();
            }
        }
    }

    @Override
    public void handleCommand(ChannelUID channelUid, Command command) {
        try {
            String groupId = channelUid.getGroupId();

            if (!channelUid.isInGroup()) {
                return;
            }
{'fraction_non_alphanumeric': 0.09506172839506173, 'fraction_numerical': 0.0028589993502274205, 'mean_word_length': 3.0059864653826134, 'pattern_counts': {'":': 4, '<': 21, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7493061', 'n_tokens_mistral': 4251, 'n_tokens_neox': 4131, 'n_words': 845}