~hww3/org.openhab.binding.nadreceiver

73b9cb7650e21b4fe7200444c17084cb9fde3b8e — H. William Welliver III 2 years ago 59c3018
sync with current binding
15 files changed, 178 insertions(+), 629 deletions(-)

M pom.xml
M src/main/history/dependencies.xml
D src/main/java/org/openhab/binding/nadreceiver/NadReceiverBindingConstants.java
D src/main/java/org/openhab/binding/nadreceiver/handler/CommunicationException.java
D src/main/java/org/openhab/binding/nadreceiver/handler/NadReceiverHandler.java
M src/main/java/org/openhab/binding/nadreceiver/internal/NadReceiverHandlerFactory.java
M src/main/java/org/openhab/binding/nadreceiver/internal/NadReceiverSourcesConfiguration.java
M src/main/java/org/openhab/binding/nadreceiver/internal/NadSourcesOptionProvider.java
D src/main/resources/ESH-INF/binding/binding.xml
D src/main/resources/ESH-INF/thing/thing-types.xml
A src/main/resources/OH-INF/binding/binding.xml
A src/main/resources/OH-INF/config/config.xml
A src/main/resources/OH-INF/i18n/nadreceiver_en_US.properties
A src/main/resources/OH-INF/thing/channel_types.xml
A src/main/resources/OH-INF/thing/thing-types.xml
M pom.xml => pom.xml +5 -4
@@ 1,12 1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>org.openhab.addons.bundles</groupId>
    <artifactId>org.openhab.addons.reactor.bundles</artifactId>
    <version>3.2.0</version>
    <version>3.3.0-SNAPSHOT</version>
  </parent>

  <artifactId>org.openhab.binding.nadreceiver</artifactId>

M src/main/history/dependencies.xml => src/main/history/dependencies.xml +1 -0
@@ 2,6 2,7 @@
<features xmlns="http://karaf.apache.org/xmlns/features/v1.6.0" name="org.openhab.binding.nadreceiver">
    <feature version="0.0.0">
        <feature>wrap</feature>
        <bundle>mvn:commons-net/commons-net/3.7.2</bundle>
        <bundle>wrap:mvn:org.lastnpe.eea/eea-all/2.2.1</bundle>
    </feature>
</features>

D src/main/java/org/openhab/binding/nadreceiver/NadReceiverBindingConstants.java => src/main/java/org/openhab/binding/nadreceiver/NadReceiverBindingConstants.java +0 -33
@@ 1,33 0,0 @@
/**
 * 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.nadreceiver;

import org.openhab.core.thing.ThingTypeUID;

/**
 * The {@link NadReceiverBinding} class defines common constants, which are
 * used across the whole binding.
 * 
 * @author Mikael Aasen - Initial contribution
 */
public class NadReceiverBindingConstants {

    public static final String BINDING_ID = "nadreceiver";

    // List of all Thing Type UIDs
    public final static ThingTypeUID THING_TYPE_RECEIVER = new ThingTypeUID(BINDING_ID, "receiver");

    // List of all Channel ids
    public final static String CHANNEL_POWER = "power";
    public final static String CHANNEL_MUTE = "mute";
    public final static String CHANNEL_VOLUME = "volume";
    public final static String CHANNEL_SOURCE = "source";
    public final static String CHANNEL_TUNER_BAND = "tuner_band";
    public final static String CHANNEL_AM_FREQUENCY = "am_frequency";
    public final static String CHANNEL_FM_FREQUENCY = "fm_frequency";
}

D src/main/java/org/openhab/binding/nadreceiver/handler/CommunicationException.java => src/main/java/org/openhab/binding/nadreceiver/handler/CommunicationException.java +0 -14
@@ 1,14 0,0 @@
package org.openhab.binding.nadreceiver.handler;

public class CommunicationException extends Exception {

    public CommunicationException(String message) {
        super(message);
    }

    public CommunicationException(String message, Exception e) {
        super(message, e);
    }

    private static final long serialVersionUID = 1L;
}

D src/main/java/org/openhab/binding/nadreceiver/handler/NadReceiverHandler.java => src/main/java/org/openhab/binding/nadreceiver/handler/NadReceiverHandler.java +0 -449
@@ 1,449 0,0 @@
/**
 * 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.nadreceiver.handler;

import static javax.measure.MetricPrefix.KILO;
import static javax.measure.MetricPrefix.MEGA;
import static org.openhab.binding.nadreceiver.NadReceiverBindingConstants.*;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.measure.quantity.Frequency;

import org.openhab.binding.nadreceiver.internal.NadReceiverDynamicStateDescriptionProvider;
import org.openhab.binding.nadreceiver.internal.PortServer;
import org.openhab.core.library.types.*;
import org.openhab.core.library.unit.Units;
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.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.StateOption;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The {@link NadReceiverHandler} is responsible for handling commands, which are
 * sent to one of the channels.
 *
 * @author Mikael Aasen - Initial contribution
 * @author William Welliver - TCP mode suppot
 */
public class NadReceiverHandler extends BaseThingHandler implements Runnable {

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

    private boolean on = false;
    private PortServer port;
    private OutputStreamWriter output;
    private InputStream input;

    private NadReceiverDynamicStateDescriptionProvider stateDescriptionProvider;

    public NadReceiverHandler(Thing thing, NadReceiverDynamicStateDescriptionProvider stateDescriptionProvider) {
        super(thing);
        this.stateDescriptionProvider = stateDescriptionProvider;
    }

    private void configureSourceStateOptions(String sources) {
        ChannelUID channelId = new ChannelUID(getThing().getUID(), CHANNEL_SOURCE);
        List<StateOption> options = new ArrayList<>();
        int i = 1;
        for (String source : sources.split(",")) {
            logger.warn("Adding state option " + i + " for " + channelId + ": " + source.trim());
            options.add(new StateOption("" + i, source.trim()));
            i++;
        }

        stateDescriptionProvider.setStateOptions(channelId, options);
    }

    @Override
    public synchronized void initialize() {
        try {
            logger.warn("thing: " + getThing());
            logger.warn("channel: " + getThing().getChannel(CHANNEL_POWER));

            configureSourceStateOptions((String) getThing().getConfiguration().get("sources"));

            ChannelUID channelUID = getUidForChannel(CHANNEL_POWER);
            boolean powerOn = isOn();
            updateState(channelUID, powerOn ? OnOffType.ON : OnOffType.OFF);
            if (powerOn) {
                refreshAll();
            } else {
                channelUID = getUidForChannel(CHANNEL_VOLUME);
                updateState(channelUID, UnDefType.UNDEF);
                channelUID = getUidForChannel(CHANNEL_MUTE);
                updateState(channelUID, UnDefType.UNDEF);
                channelUID = getUidForChannel(CHANNEL_SOURCE);
                updateState(channelUID, UnDefType.UNDEF);
                channelUID = getUidForChannel(CHANNEL_TUNER_BAND);
                updateState(channelUID, UnDefType.UNDEF);
                channelUID = getUidForChannel(CHANNEL_AM_FREQUENCY);
                updateState(channelUID, UnDefType.UNDEF);
                channelUID = getUidForChannel(CHANNEL_FM_FREQUENCY);
                updateState(channelUID, UnDefType.UNDEF);
                updateStatus(ThingStatus.ONLINE);
            }
        } catch (IOException e) {
            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
        }
        BigDecimal pollingInterval = (BigDecimal) getThing().getConfiguration().get("polling-interval");
        if (pollingInterval != null && pollingInterval.longValue() != 0) {
            scheduler.scheduleAtFixedRate(this, pollingInterval.longValue(), pollingInterval.longValue(),
                    TimeUnit.SECONDS);
        }
    }

    private void refreshAll() throws IOException {
        ChannelUID channelUID;
        try {
            channelUID = getUidForChannel(CHANNEL_VOLUME);
            updateState(channelUID, PercentType.valueOf(Double.toString(toPercent(askInt("Main.Volume")))));
            boolean isMute = "On".equals(askString("Main.Mute", false));
            channelUID = getUidForChannel(CHANNEL_MUTE);
            updateState(channelUID, isMute ? OnOffType.ON : OnOffType.OFF);
            channelUID = getUidForChannel(CHANNEL_SOURCE);
            updateState(channelUID, new StringType(Integer.toString(askInt("Main.Source"))));
            channelUID = getUidForChannel(CHANNEL_SOURCE);
            updateState(channelUID, new StringType(Integer.toString(askInt("Main.Source"))));
            channelUID = getUidForChannel(CHANNEL_TUNER_BAND);
            updateState(channelUID, new StringType(askString("Tuner.Band", false)));
            channelUID = getUidForChannel(CHANNEL_AM_FREQUENCY);
            updateState(channelUID,
                    new QuantityType<Frequency>(BigDecimal.valueOf(askInt("Tuner.AM.Frequency")), KILO(Units.HERTZ)));
            channelUID = getUidForChannel(CHANNEL_FM_FREQUENCY);
            updateState(channelUID,
                    new QuantityType<Frequency>(BigDecimal.valueOf(askFloat("Tuner.FM.Frequency")), MEGA(Units.HERTZ)));

        } catch (CommunicationException e) {
            logger.warn("Error communicating with receiver.", e);
            port.disconnect();
        }
    }

    private ChannelUID getUidForChannel(String channelName) {
        return getThing().getChannel(channelName).getUID();
    }

    @Override
    public void dispose() {
        try {
            port.disconnect();
        } catch (NullPointerException e) {
        }
    }

    private synchronized void connect() {
        if (port == null || !port.isConnected()) {
            logger.warn("connecting");

            String hostName = (String) getThing().getConfiguration().get("hostName");
            BigDecimal portNumber = (BigDecimal) getThing().getConfiguration().get("tcpPort");

            if (hostName == null || hostName.isEmpty()) {
                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No host name specified.");
                return;
            }

            if (portNumber == null || portNumber.toBigInteger().intValue() == 0) {
                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No TCP port specified.");
                return;
            }

            port = new PortServer();
            port.setTcpMode(hostName, portNumber.toBigInteger().intValue());

            try {
                port.connect();
                OutputStream outStream = port.getOutputStream();
                output = new OutputStreamWriter(outStream);
                input = port.getInputStream();
            } catch (IOException e) {
                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
                logger.warn("Error connecting to receiver.", e);

            }
        }
    }

    private double toPercent(double decibel) {
        return (decibel + 92) * 0.9;
    }

    private double toDecibel(double percent) {
        return (percent / 0.9) - 92;
    }

    @Override
    public void handleCommand(ChannelUID channelUID, Command command) {
        try {
            // logger.warn("configuration: " + this.getThing().getChannel(CHANNEL_SOURCE).getConfiguration());
            if (channelUID.getId().equals(CHANNEL_POWER)) {
                handlePower(channelUID, command);
            } else if (channelUID.getId().equals(CHANNEL_VOLUME)) {
                handleVolume(channelUID, command);
            } else if (channelUID.getId().equals(CHANNEL_MUTE)) {
                handleMute(channelUID, command);
            } else if (channelUID.getId().equals(CHANNEL_SOURCE)) {
                handleSource(channelUID, command);
            } else if (channelUID.getId().equals(CHANNEL_TUNER_BAND)) {
                handleTunerBand(channelUID, command);
            } else if (channelUID.getId().equals(CHANNEL_AM_FREQUENCY)) {
                handleAMFrequency(channelUID, command);
            } else if (channelUID.getId().equals(CHANNEL_FM_FREQUENCY)) {
                handleFMFrequency(channelUID, command);
            }
        } catch (IOException | CommunicationException e) {
            port.disconnect();
            port = null;
            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
            logger.error("Port port error: " + e.getMessage());
            on = false;
        }
    }

    private void handleMute(ChannelUID channelUID, Command command) throws CommunicationException, IOException {
        if (command != null) {
            if (command instanceof OnOffType) {
                if (command.equals(OnOffType.ON)) {
                    setValue("Main.Mute", "On");
                } else if (command.equals(OnOffType.OFF)) {
                    setValue("Main.Mute", "Off");
                }
            } else if (command instanceof RefreshType) {
                boolean isMute = "On".equals(askString("Main.Mute", false));
                updateState(channelUID, isMute ? OnOffType.ON : OnOffType.OFF);
            }
        }
    }

    private void handleSource(ChannelUID channelUID, Command command) throws CommunicationException, IOException {
        if (command != null) {
            if (command instanceof RefreshType) {
                updateState(channelUID, new StringType(Integer.toString(askInt("Main.Source"))));
            } else if (command instanceof StringType) {
                setValue("Main.Source", command.toString());
            }
        }
    }

    private void handleTunerBand(ChannelUID channelUID, Command command) throws CommunicationException, IOException {
        if (command != null) {
            if (command instanceof RefreshType) {
                updateState(channelUID, new StringType(askString("Tuner.Band", false)));
            } else if (command instanceof StringType) {
                setValue("Tuner.Band", command.toString());
            }
        }
    }

    private void handleAMFrequency(ChannelUID channelUID, Command command) throws CommunicationException, IOException {
        if (command != null) {
            if (command instanceof RefreshType) {
                updateState(channelUID, new QuantityType<Frequency>(BigDecimal.valueOf(askInt("Tuner.AM.Frequency")),
                        KILO(Units.HERTZ)));
            } else if (command instanceof QuantityType) {
                QuantityType t = (QuantityType) command;
                t = t.toUnit(KILO(Units.HERTZ));
                setValue("Tuner.AM.Frequency", "" + t.intValue());
            }
        }
    }

    private void handleFMFrequency(ChannelUID channelUID, Command command) throws CommunicationException, IOException {
        if (command != null) {
            if (command instanceof RefreshType) {
                updateState(channelUID, new QuantityType<Frequency>(BigDecimal.valueOf(askInt("Tuner.AM.Frequency")),
                        KILO(Units.HERTZ)));
            } else if (command instanceof QuantityType) {
                QuantityType t = (QuantityType) command;
                t = t.toUnit(MEGA(Units.HERTZ));
                t.toBigDecimal().setScale(1).toPlainString();
                setValue("Tuner.AM.Frequency", t.toBigDecimal().setScale(1).toPlainString());
            }
        }
    }

    private void handlePower(ChannelUID channelUID, Command command) throws IOException, CommunicationException {
        logger.info("Setting power to: " + command.toString());
        if (command != null) {
            if (command instanceof OnOffType) {
                if (command.equals(OnOffType.ON)) {
                    on();
                } else if (command.equals(OnOffType.OFF)) {
                    off();
                }
            } else if (command instanceof RefreshType) {
                updateState(channelUID, isOn() ? OnOffType.ON : OnOffType.OFF);
            }
        }
    }

    private void handleVolume(ChannelUID channelUID, Command command) throws CommunicationException, IOException {
        if (command != null) {
            if (command instanceof IncreaseDecreaseType && command == IncreaseDecreaseType.INCREASE) {
                adjustValue("Main.Volume", "+");
            } else if (command instanceof IncreaseDecreaseType && command == IncreaseDecreaseType.DECREASE) {
                adjustValue("Main.Volume", "-");
            } else if (command instanceof DecimalType) {
                setValue("Main.Volume", Integer.toString((int) toDecibel(Double.parseDouble(command.toString()))));
            } else if (command instanceof RefreshType) {
                updateState(channelUID, PercentType.valueOf(Integer.toString((int) toPercent(askInt("Main.Volume")))));
            }
        }
    }

    public void off() throws IOException, CommunicationException {
        connect();
        setValue("Main.Power", "Off");
    }

    public synchronized void on() throws IOException {
        connect();
        int attempts = 0;
        do {
            output.write("\rMain.Power=On\r");
            output.flush();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                return;
            }
        } while (!isOn() && attempts++ < 3);
        if (isOn()) {
            refreshAll();
        }
    }

    public boolean isOn() throws IOException {
        String power;
        try {
            power = askString("Main.Power", true);
        } catch (CommunicationException e) {
            power = "";
        }
        on = "On".equals(power);
        return on;
    }

    private String askString(String key, boolean ignorePowerState) throws CommunicationException, IOException {
        String answer = requestResponse(key + "?", ignorePowerState);
        String[] parts = answer.split("=");
        if (parts.length == 2) {
            updateStatus(ThingStatus.ONLINE);
            return parts[1];
        }
        throw new CommunicationException("Invalid response from receiver");
    }

    private int askInt(String key) throws CommunicationException, IOException {
        try {
            return Integer.parseInt(askString(key, false));
        } catch (NumberFormatException e) {
            throw new CommunicationException("Not a number received", e);
        }
    }

    private float askFloat(String key) throws CommunicationException, IOException {
        try {
            return Float.parseFloat(askString(key, false));
        } catch (NumberFormatException e) {
            throw new CommunicationException("Not a number received", e);
        }
    }

    private void setValue(String key, String value) throws CommunicationException, IOException {
        String answer = requestResponse(key + "=" + value, false);
        String[] parts = answer.split("=");
        if (parts.length != 2) {
            throw new CommunicationException("Invalid response from receiver: (length=" + Integer.toString(parts.length)
                    + ") key \"" + parts[0] + "\" answer " + answer + " expected \"" + key + "\"");
        }
    }

    private String adjustValue(String key, String operator) throws CommunicationException, IOException {
        String answer = requestResponse(key + operator, false);
        String[] parts = answer.split("=");
        if (parts.length == 2 && key.equals(parts[0])) {
            return parts[1];
        } else {
            throw new CommunicationException("Got the wrong answer: " + answer);
        }
    }

    private synchronized String requestResponse(String request, boolean ignorePowerState)
            throws IOException, CommunicationException {
        if (!on && !ignorePowerState && !isOn()) {
            throw new CommunicationException("Receiver is off");
        }
        connect();

        while (input != null && input.available() > 0) {
            input.read();
        }
        logger.debug("sending to receiver: " + request);
        output.write("\r" + request + "\r");
        output.flush();
        int data = input.read();

        if (data != -1) {
            // logger.warn("got response from receiver: " + data);

            boolean end = false;
            String answer = "";

            if (data != '\r') {
                answer += (char) data;
            }

            while (!end) {
                data = input.read();
                if (data == '\r' || data == '\n') {
                    end = true;
                } else if (data == -1) {
                    throw new CommunicationException("Unexpected end of input");
                } else {
                    answer += (char) data;
                }
            }
            logger.debug("got response from receiver: " + answer);
            return answer;
        }
        throw new CommunicationException("Invalid response from receiver");
    }

    @Override
    public void run() {
        ChannelUID channelUID = getUidForChannel(CHANNEL_POWER);
        try {
            boolean power = isOn();
            updateStatus(ThingStatus.ONLINE);
            updateState(channelUID, power ? OnOffType.ON : OnOffType.OFF);
            if (power) {
                refreshAll();
            }
        } catch (IOException e) {
            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
            logger.warn("Error communicating with receiver.", e);
            port.disconnect();
        }
    }
}

M src/main/java/org/openhab/binding/nadreceiver/internal/NadReceiverHandlerFactory.java => src/main/java/org/openhab/binding/nadreceiver/internal/NadReceiverHandlerFactory.java +0 -1
@@ 22,7 22,6 @@ import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;


M src/main/java/org/openhab/binding/nadreceiver/internal/NadReceiverSourcesConfiguration.java => src/main/java/org/openhab/binding/nadreceiver/internal/NadReceiverSourcesConfiguration.java +1 -1
@@ 72,7 72,7 @@ public class NadReceiverSourcesConfiguration {
        }
        List<StateOption> options = new ArrayList<StateOption>();
        for (NadReceiverSourceConfiguration config : configurations.values()) {
            logger.info("config: " + config);
            logger.trace("config: " + config);
            if (config != null && config.getEnabled()) {
                options.add(new StateOption(config.getNumber(), config.getName()));
            }

M src/main/java/org/openhab/binding/nadreceiver/internal/NadSourcesOptionProvider.java => src/main/java/org/openhab/binding/nadreceiver/internal/NadSourcesOptionProvider.java +2 -2
@@ 49,7 49,7 @@ public class NadSourcesOptionProvider implements DynamicStateDescriptionProvider
    @Override
    public @Nullable StateDescription getStateDescription(Channel channel, @Nullable StateDescription original,
            @Nullable Locale locale) {
        logger.info("getStateDescription for " + channel.getUID() + " ");
        logger.trace("getStateDescription for " + channel.getUID() + " ");

        List<StateOption> options = channelOptionsMap.get(channel.getUID());



@@ 57,7 57,7 @@ public class NadSourcesOptionProvider implements DynamicStateDescriptionProvider
            return null;
        }

        logger.info("getStateDescription for " + channel + " " + options);
        logger.trace("getStateDescription for " + channel + " " + options);
        if (original != null) {
            return StateDescriptionFragmentBuilder.create(original).withOptions(options).build().toStateDescription();
        }

D src/main/resources/ESH-INF/binding/binding.xml => src/main/resources/ESH-INF/binding/binding.xml +0 -10
@@ 1,10 0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="nadreceiver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:binding="http://eclipse.org/smarthome/schemas/binding/v1.0.0"
	xsi:schemaLocation="http://eclipse.org/smarthome/schemas/binding/v1.0.0 http://eclipse.org/smarthome/schemas/binding-1.0.0.xsd">

	<name>NAD Receiver</name>
	<description>Binding for RS232/TCP connection to NAD receiver.</description>
	<author>Mikael Aasen</author>

</binding:binding>

D src/main/resources/ESH-INF/thing/thing-types.xml => src/main/resources/ESH-INF/thing/thing-types.xml +0 -115
@@ 1,115 0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="nadreceiver"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:thing="http://eclipse.org/smarthome/schemas/thing-description/v1.0.0"
	xsi:schemaLocation="http://eclipse.org/smarthome/schemas/thing-description/v1.0.0 http://eclipse.org/smarthome/schemas/thing-description-1.0.0.xsd">


	<!-- Sample Thing Type -->
	<thing-type id="receiver">
		<label>Receiver</label>
		<description>Receiver control</description>

		<channels>
			<channel id="power" typeId="power"/>
			<channel id="volume" typeId="volume"/>
			<channel id="mute" typeId="mute"/>
			<channel id="source" typeId="source"/>
		</channels>

		<config-description>
			<parameter name="isTcpPort" type="boolean">
				<label>Use TCP?</label>
				<description>Is receiver connected via TCP?</description>
				<default>false</default>
			</parameter>
			<parameter name="port" type="text">
				<label>COM port</label>
				<description>Select serial port</description>
				<default>COM1</default>
				<context>serial-port</context>
			</parameter>
			<parameter name="hostName" type="text">
				<label>Host Name</label>
				<description>Host name of receiver</description>
				<default>localhost</default>
				<context>network-address</context>
			</parameter>
			<parameter name="tcpPort" type="integer" max="65535">
				<label>Port Number</label>
				<description>Network port number of receiver</description>
				<default>1234</default>
			</parameter>
			<parameter name="polling-interval" type="integer">
				<label>polling Interval</label>
				<description>Select polling Interval</description>
				<default>60</default>
			</parameter>
			<parameter name="sources" type="text">
				<label>Source Labels</label>
				<description>Comma separated list of descriptions for input sources</description>
				<default>Source 1, Source 2, Source 3, Source 4, Source 5, Source 6, Source 7, Source 8, Source 9</default>
				<context>network-address</context>
			</parameter>
		</config-description>
	</thing-type>

	<!-- Sample Channel Type -->
	<channel-type id="power">
		<item-type>Switch</item-type>
		<label>Power switch</label>
		<description>Turn it on or off.</description>
		<category>Power</category>
	</channel-type>

	<channel-type id="mute">
		<item-type>Switch</item-type>
		<label>Mute</label>
		<description>Mute on/off.</description>
		<category>Mute</category>
	</channel-type>

	<channel-type id="volume">
		<item-type>Dimmer</item-type>
		<label>Volume</label>
		<description>Volume control</description>
		<category>SoundVolume</category>
	</channel-type>

	<channel-type id="source">
		<item-type>String</item-type>
		<label>Source</label>
		<description>Select input</description>
		<category>Source</category>
		<!-- options are provided by NadReceiverDynamicStateDescriptionProvider.java -->
	</channel-type>

	<channel-type id="tuner_band">
		<item-type>String</item-type>
		<label>Tuner Band</label>
		<description>Select Band</description>
		<category>Source</category>
		<state>
			<option>FM</option>
			<option>AM</option>
			<option>DAB</option>
			<option>XM</option>
		</state>
	</channel-type>

	<channel-type id="am_frequency">
		<item-type>Number:Frequency</item-type>
		<label>AM Frequency</label>
		<description>Select Frequency</description>
		<category>Source</category>
		<state readOnly="false" min="550" max="1700" pattern="%d %unit%"/>
	</channel-type>

	<channel-type id="fm_frequency">
		<item-type>Number:Frequency</item-type>
		<label>FM Frequency</label>
		<description>Select Frequency</description>
		<category>Source</category>
		<state readOnly="false" min="88.0" max="108.0" pattern="%.1f %unit%"/>
	</channel-type>
</thing:thing-descriptions>

A src/main/resources/OH-INF/binding/binding.xml => src/main/resources/OH-INF/binding/binding.xml +10 -0
@@ 0,0 1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="nadreceiver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
	xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">

	<name>NAD Receiver Binding</name>
	<description>This is the binding for NAD Receivers (e.g. T758v3)</description>
	<author>Marc Chételat</author>

</binding:binding>

A src/main/resources/OH-INF/config/config.xml => src/main/resources/OH-INF/config/config.xml +40 -0
@@ 0,0 1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:config-description="http://eclipse.org/smarthome/schemas/config-description/v1.0.0"
	xsi:schemaLocation="http://eclipse.org/smarthome/schemas/config-description/v1.0.0 http://eclipse.org/smarthome/schemas/config-description-1.0.0.xsd">

	<config-description uri="thing-type:nadreceiver:config">
		<parameter name="hostname" type="text">
			<label>Network Address</label>
			<description>The IP or host name of the NAD Receiver</description>
			<context>network-address</context>
		</parameter>
		<parameter name="port" type="integer" min="1" max="65335">
			<label>Port</label>
			<description>Port for the NAD Receiver device to control. It uses telnet and therefore by default use port 23.</description>
			<default>23</default>
		</parameter>
		<parameter name="reconnectInterval" type="integer" min="1" max="60" unit="min">
			<label>Reconnect Interval</label>
			<description>The period in minutes that the handler will wait between connection attempts</description>
			<unitLabel>minutes</unitLabel>
			<default>5</default>
			<advanced>true</advanced>
		</parameter>
		<parameter name="heartbeatInterval" type="integer" min="1" max="60" unit="min">
			<label>Keepalive Heartbeat Interval</label>
			<description>The period in minutes between connection heartbeat checks</description>
			<unitLabel>minutes</unitLabel>
			<default>5</default>
			<advanced>true</advanced>
		</parameter>
		<parameter name="maxSources" type="integer" min="1" max="60" unit="min">
			<label>Maximum number of sources</label>
			<description>The binding will iterate from 1 to max. number of sources to detect them</description>
			<unitLabel>sources</unitLabel>
			<default>9</default>
			<advanced>true</advanced>
		</parameter>
	</config-description>
</config-description:config-descriptions>

A src/main/resources/OH-INF/i18n/nadreceiver_en_US.properties => src/main/resources/OH-INF/i18n/nadreceiver_en_US.properties +13 -0
@@ 0,0 1,13 @@
# FIXME: please substitute the xx_XX with a proper locale, ie. de_DE
# FIXME: please do not add the file to the repo if you add or change no content
# binding
binding.nadreceiver.name = NAD Receiver
binding.nadreceiver.description = Support for NAD T series amplifiers and processors

# thing types
thing-type.nadreceiver.sample.label = <Your localized Thing label>
thing-type.nadreceiver.sample.description = <Your localized Thing description>

# channel types
channel-type.nadreceiver.sample-channel.label = <Your localized Channel label>
channel-type.nadreceiver.sample-channel.description = <Your localized Channel description>

A src/main/resources/OH-INF/thing/channel_types.xml => src/main/resources/OH-INF/thing/channel_types.xml +78 -0
@@ 0,0 1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="nadreceiver"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
	xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">

	<!-- Commands -->
	<channel-type id="model">
		<item-type>String</item-type>
		<label>Model</label>
		<description>Model name of the device</description>
		<category>Information</category>
	</channel-type>
	<channel-type id="power">
		<item-type>Switch</item-type>
		<label>Power</label>
		<description>Power on/off your device</description>
		<category>Switch</category>
	</channel-type>
	<channel-type id="volume">
		<item-type>Dimmer</item-type>
		<label>Volume</label>
		<description>Volume level of the TV.</description>
		<category>SoundVolume</category>
	</channel-type>
	<channel-type id="mute">
		<item-type>Switch</item-type>
		<label>Mute</label>
		<description>Mute state of the TV.</description>
		<category>Switch</category>
	</channel-type>
	<channel-type id="tuner_band">
		<item-type>String</item-type>
		<label>Tuner Band</label>
		<description>Selected Tuner Band</description>
		<category>Options</category>
		<state>
			<options>
				<option value="AM">AM</option>
				<option value="FM">FM</option>
				<option value="XM">SiriusXM</option>
			</options>
		</state>
	</channel-type>
	<channel-type id="am_mute">
		<item-type>Switch</item-type>
		<label>AM Mute</label>
		<description>AM Tuner Mute</description>
		<category>Options</category>
	</channel-type>
	<channel-type id="fm_mute">
		<item-type>Switch</item-type>
		<label>FM Mute</label>
		<description>FM Tuner Mute</description>
		<category>Options</category>
	</channel-type>
	<channel-type id="fm_frequency">
		<item-type>Number:Frequency</item-type>
		<label>FM Frequency</label>
		<description>FM Tuner Frequency</description>
		<category>Options</category>
		<state pattern="%.2f %unit%"></state>
	</channel-type>
	<channel-type id="am_frequency">
		<item-type>Number:Frequency</item-type>
		<label>AM Frequency</label>
		<description>AM Tuner Frequency</description>
		<category>Options</category>
		<state pattern="%f %unit%"></state>
	</channel-type>
	<!-- Will get dynamic state options -->
	<channel-type id="source">
		<item-type>String</item-type>
		<label>Input Source</label>
		<description>Select the input source of the receiver</description>
		<category>Options</category>
	</channel-type>
</thing:thing-descriptions>

A src/main/resources/OH-INF/thing/thing-types.xml => src/main/resources/OH-INF/thing/thing-types.xml +28 -0
@@ 0,0 1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="nadreceiver"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
	xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
	<!-- Sample Thing Type -->
	<thing-type id="receiver">
		<label>NadReceiver Binding Thing</label>
		<description>Thing for NadReceiver Binding</description>

		<channels>
			<channel id="model" typeId="model"/>
			<channel id="power" typeId="power"/>
			<channel id="volume" typeId="volume"/>
			<channel id="mute" typeId="mute"/>
			<channel id="source" typeId="source"/>

			<channel id="tuner_band" typeId="tuner_band"/>
			<channel id="am_frequency" typeId="am_frequency"/>
			<channel id="fm_frequency" typeId="fm_frequency"/>
			<channel id="am_mute" typeId="am_mute"/>
			<channel id="fm_mute" typeId="fm_mute"/>
		</channels>

		<config-description-ref uri="thing-type:nadreceiver:config"/>
	</thing-type>

</thing:thing-descriptions>