[yamahamusiccast] Initial contribution (#11880)

* Pull Request OH3

Signed-off-by: Lennert Coopman <github@coopman.org>

* Pull Request OH3

Signed-off-by: Lennert Coopman <github@coopman.org>

* Pull Request OH3

Signed-off-by: Lennert Coopman <github@coopman.org>

* Pull Request OH3

Signed-off-by: Lennert Coopman <github@coopman.org>

* Pull Request OH3

Signed-off-by: Lennert Coopman <github@coopman.org>

* Pull Request OH3

Signed-off-by: Lennert Coopman <github@coopman.org>

* Pull Request OH3

Signed-off-by: Lennert Coopman <github@coopman.org>

* Pull Request OH3

Signed-off-by: Lennert Coopman <github@coopman.org>
This commit is contained in:
coop-git 2022-01-06 20:04:25 +01:00 committed by GitHub
parent da59cdd255
commit 60c1eceb96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 2725 additions and 0 deletions

View File

@ -341,6 +341,7 @@
/bundles/org.openhab.binding.wolfsmartset/ @BoBiene /bundles/org.openhab.binding.wolfsmartset/ @BoBiene
/bundles/org.openhab.binding.xmltv/ @clinique /bundles/org.openhab.binding.xmltv/ @clinique
/bundles/org.openhab.binding.xmppclient/ @pavel-gololobov /bundles/org.openhab.binding.xmppclient/ @pavel-gololobov
/bundles/org.openhab.binding.yamahamusiccast/ @coop-git
/bundles/org.openhab.binding.yamahareceiver/ @davidgraeff @zarusz /bundles/org.openhab.binding.yamahareceiver/ @davidgraeff @zarusz
/bundles/org.openhab.binding.yeelight/ @claell /bundles/org.openhab.binding.yeelight/ @claell
/bundles/org.openhab.binding.yioremote/ @miloit /bundles/org.openhab.binding.yioremote/ @miloit

View File

@ -1696,6 +1696,11 @@
<artifactId>org.openhab.binding.xmppclient</artifactId> <artifactId>org.openhab.binding.xmppclient</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.yamahamusiccast</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.yamahareceiver</artifactId> <artifactId>org.openhab.binding.yamahareceiver</artifactId>

View File

@ -0,0 +1,168 @@
# Yamaha MusicCast Binding
Binding to control Yamaha models via their MusicCast protocol (aka Yamaha Extended Control).
With support for 4 zones : main, zone2, zone3, zone4. Main is always present. Zone2, Zone3, Zone4 are read from the model.
UDP events are captured to reflect changes in the binding for
- Power
- Mute
- Volume
- Input
- Presets
- Sleep
- Artist
- Track
- Album
- Album Art
- Repeat
- Shuffle
- Play Time
- Total Time
- Musiccast Link
## Supported Things
Each model (AV Receiver, ...) is a Thing (Thing Type ID: yamahamusiccast:device). Things are linked to a Bridge (Thing Type ID: yamahamusiccast:bridge) for receiving UDP events.
## Discovery
No auto discovery
## Thing Configuration
| Parameter | Type | Description | Advanced | Required |
|--------------------|---------|---------------------------------------------------------|----------|---------------|
| host | String | IP address of the Yamaha model (AVR, ...) | false | true |
| syncVolume | Boolean | Sync volume across linked models (default=false) | false | false |
| defaultAfterMCLink | String | Default Input value for client when MC Link is broken | false | false |
Default value for *defaultAfterMCLink* is *NET RADIO* as most of the models have this on board.
## Channels
| channel | type | description |
|----------------|--------|---------------------------------------------------------------------|
| power | Switch | Power ON/OFF |
| mute | Switch | Mute ON/OFF |
| volume | Dimmer | Volume as % (recalculated based on Max Volume Model) |
| volumeAbs | Number | Volume as absolute value |
| input | String | See below for list |
| soundProgram | String | See below for list |
| selectPreset | String | Select Netradio/USB preset (fetched from Model) |
| sleep | Number | Fixed values for Sleep : 0/30/60/90/120 in minutes |
| recallScene | Number | Select a scene (8 defaults scenes are foreseen) |
| player | Player | PLAY/PAUSE/NEXT/PREVIOUS/REWIND/FASTFORWARD |
| artist | String | Artist |
| track | String | Track |
| album | String | Album |
| albumArt | Image | Album Art |
| repeat | String | Toggle Repeat. Available values: Off, One, All |
| shuffle | String | Toggle Shuffle. Available values: Off, On, Songs, Album |
| playTime | String | Play time of current selection: radio, song, track, ... |
| totalTime | String | Total time of current selection: radio, song, track, ... |
| mclinkStatus | String | Select your Musiccast Server or set to Standalone, Server or Client |
| Zones | description |
|----------------------|------------------------------------------------------|
| zone1-4 | Zone 1 to 4 to control Power, Volume, ... |
| playerControls | Separate zone for Play, Pause, ... |
## Input List
Firmware v1
cd / tuner / multi_ch / phono / hdmi1 / hdmi2 / hdmi3 / hdmi4 / hdmi5 / hdmi6 / hdmi7 /
hdmi8 / hdmi / av1 / av2 / av3 / av4 / av5 / av6 / av7 / v_aux / aux1 / aux2 / aux / audio1 /
audio2 / audio3 / audio4 / audio_cd / audio / optical1 / optical2 / optical / coaxial1 / coaxial2 /
coaxial / digital1 / digital2 / digital / line1 / line2 / line3 / line_cd / analog / tv / bd_dvd /
usb_dac / usb / bluetooth / server / net_radio / rhapsody / napster / pandora / siriusxm /
spotify / juke / airplay / radiko / qobuz / mc_link / main_sync / none
Firmware v2
cd / tuner / multi_ch / phono / hdmi1 / hdmi2 / hdmi3 / hdmi4 / hdmi5 / hdmi6 / hdmi7 /
hdmi8 / hdmi / av1 / av2 / av3 / av4 / av5 / av6 / av7 / v_aux / aux1 / aux2 / aux / audio1 /
audio2 / audio3 / audio4 / **audio5** / audio_cd / audio / optical1 / optical2 / optical / coaxial1 / coaxial2 /
coaxial / digital1 / digital2 / digital / line1 / line2 / line3 / line_cd / analog / tv / bd_dvd /
usb_dac / usb / bluetooth / server / net_radio / ~~rhapsody~~ /napster / pandora / siriusxm /
spotify / juke / airplay / radiko / qobuz / **tidal** / **deezer** / mc_link / main_sync / none
## Sound Program
munich_a / munich_b / munich / frankfurt / stuttgart / vienna / amsterdam / usa_a / usa_b /
tokyo / freiburg / royaumont / chamber / concert / village_gate / village_vanguard /
warehouse_loft / cellar_club / jazz_club / roxy_theatre / bottom_line / arena / sports /
action_game / roleplaying_game / game / music_video / music / recital_opera / pavilion /
disco / standard / spectacle / sci-fi / adventure / drama / talk_show / tv_program /
mono_movie / movie / enhanced / 2ch_stereo / 5ch_stereo / 7ch_stereo / 9ch_stereo /
11ch_stereo / stereo / surr_decoder / my_surround / target / straight / off
## Full Example
### Bridge & Thing(s)
```
Bridge yamahamusiccast:bridge:virtual "YXC Bridge" {
Thing yamahamusiccast:device:Living "YXC Living" [host="1.2.3.4"]
}
```
### Basic setup
```
Switch YamahaPower "" {channel="yamahamusiccast:device:Living:main#power"}
Switch YamahaMute "" {channel="yamahamusiccast:device:Living:main#mute"}
Dimmer YamahaVolume "" {channel="yamahamusiccast:device:Living:main#volume"}
Number YamahaVolumeAbs "" {channel="yamahamusiccast:device:Living:main#volumeAbs"}
String YamahaInput "" {channel="yamahamusiccast:device:Living:main#input"}
String YamahaSelectPreset "" {channel="yamahamusiccast:device:Living:main#selectPreset"}
String YamahaSoundProgram "" {channel="yamahamusiccast:device:Living:main#soundProgram"}
```
### Player controls
```
Player YamahaPlayer "" {channel="yamahamusiccast:device:Living:playerControls#player"}
String YamahaArt "" {channel="yamahamusiccast:device:Living:playerControls#albumArt"}
String YamahaArtist "" {channel="yamahamusiccast:device:Living:playerControls#artist"}
String YamahaTrack "" {channel="yamahamusiccast:device:Living:playerControls#track"}
String YamahaAlbum "" {channel="yamahamusiccast:device:Living:playerControls#album"}
```
### MusicCast setup
The idea here is to select what device/model will be the master. This needs to be done per device/model which will then be the slave.
If you want the *Living* to be the master for the *Kitchen*, select *Living - zone (IP)* from the thing *Kitchen*.
The binding will check if there is already a group active for which *Living* is the master. If yes, this group will be used and *Kitchen* will be added.
If not, a new group will be created.
*Device A*: Living with IP 192.168.1.1
*Device B*: Kitchen with IP 192.168.1.2
Set **mclinkStatus** to *Standalone* to remove the device/model from the current active group. The group will keep on exist with other devices/models.
If the device/model is the server, the group will be disbanded.
```
String YamahaMCLinkStatus "" {channel="yamahamusiccast:device:Living:main#mclinkStatus"}
```
During testing with the Yamaha Musiccast app, when removing a slave from the group, the status of the client remained *client* and **input** stayed on *mclink*. Only when changing input, the slave was set to *standalone*. Therefor you can set the parameter **defaultAfterMCLink** to an input value supported by your device to break the whole Musiccast Link in OH.
#### How to use this in a rule?
The label uses the format _Thinglabel - zone (IP)_.
The value which is sent to OH uses the format _IP***zone_.
```
sendCommand(Kitchen_YamahaMCServer, "192.168.1.1***main")
sendCommand(Kitchen_YamahaMCServer, "")
sendCommand(Kitchen_YamahaMCServer, "server")
sendCommand(Kitchen_YamahaMCServer, "client")
```
## Tested Models
RX-D485 / WX-010 / WX-030 / ISX-80 / YSP-1600 / RX-A860 / R-N303D / EX-A1080 / WXA-050 / HTR-4068 (RX-V479)
MusicCast 20 / WCX-50 / RX-V6A / YAS-306 / ISX-18D / WX-021 / YAS-408

View File

@ -0,0 +1,17 @@
<?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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.yamahamusiccast</artifactId>
<name>openHAB Add-ons :: Bundles :: Yamaha Musiccast Binding</name>
</project>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.yamahamusiccast-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-yamahamusiccast" description="Yamaha Musiccast Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<feature>openhab-transport-upnp</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.yamahamusiccast/${project.version}</bundle>
</feature>
</features>

View File

@ -0,0 +1,71 @@
/**
* Copyright (c) 2010-2021 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.yamahamusiccast.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.type.ChannelTypeUID;
/**
* The {@link YamahaMusiccastBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Lennert Coopman - Initial contribution
*/
@NonNullByDefault
public class YamahaMusiccastBindingConstants {
private static final String BINDING_ID = "yamahamusiccast";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_DEVICE = new ThingTypeUID(BINDING_ID, "device");
public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge");
// List of all Channel Type UIDs
public static final ChannelTypeUID CHANNEL_TYPE_UID_POWER = new ChannelTypeUID("system:power");
public static final ChannelTypeUID CHANNEL_TYPE_UID_MUTE = new ChannelTypeUID("system:mute");
public static final ChannelTypeUID CHANNEL_TYPE_UID_VOLUME = new ChannelTypeUID("system:volume");
public static final ChannelTypeUID CHANNEL_TYPE_UID_VOLUMEABS = new ChannelTypeUID(BINDING_ID, "volumeAbs");
public static final ChannelTypeUID CHANNEL_TYPE_UID_INPUT = new ChannelTypeUID(BINDING_ID, "input");
public static final ChannelTypeUID CHANNEL_TYPE_UID_SOUNDPROGRAM = new ChannelTypeUID(BINDING_ID, "soundProgram");
public static final ChannelTypeUID CHANNEL_TYPE_UID_SELECTPRESET = new ChannelTypeUID(BINDING_ID, "selectPreset");
public static final ChannelTypeUID CHANNEL_TYPE_UID_SLEEP = new ChannelTypeUID(BINDING_ID, "sleep");
public static final ChannelTypeUID CHANNEL_TYPE_UID_RECALLSCENE = new ChannelTypeUID(BINDING_ID, "recallScene");
public static final ChannelTypeUID CHANNEL_TYPE_UID_MCLINKSTATUS = new ChannelTypeUID(BINDING_ID, "mclinkStatus");
// List of all Channel ids
public static final String CHANNEL_POWER = "power";
public static final String CHANNEL_MUTE = "mute";
public static final String CHANNEL_VOLUME = "volume";
public static final String CHANNEL_VOLUMEABS = "volumeAbs";
public static final String CHANNEL_INPUT = "input";
public static final String CHANNEL_SOUNDPROGRAM = "soundProgram";
public static final String CHANNEL_SELECTPRESET = "selectPreset";
public static final String CHANNEL_PLAYER = "player";
public static final String CHANNEL_SLEEP = "sleep";
public static final String CHANNEL_RECALLSCENE = "recallScene";
public static final String CHANNEL_ARTIST = "artist";
public static final String CHANNEL_TRACK = "track";
public static final String CHANNEL_ALBUM = "album";
public static final String CHANNEL_ALBUMART = "albumArt";
public static final String CHANNEL_REPEAT = "repeat";
public static final String CHANNEL_SHUFFLE = "shuffle";
public static final String CHANNEL_MCLINKSTATUS = "mclinkStatus";
public static final String CHANNEL_PLAYTIME = "playTime";
public static final String CHANNEL_TOTALTIME = "totalTime";
public static final int CONNECTION_TIMEOUT_MILLISEC = 5000;
public static final int LONG_CONNECTION_TIMEOUT_MILLISEC = 60000;
public static final String HTTP = "http://";
public static final String YAMAHA_EXTENDED_CONTROL = "/YamahaExtendedControl/v1/";
}

View File

@ -0,0 +1,158 @@
/**
* Copyright (c) 2010-2021 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.yamahamusiccast.internal;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.yamahamusiccast.internal.dto.UdpMessage;
import org.openhab.core.common.NamedThreadFactory;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
/**
* The {@link YamahaMusiccastBridgeHandler} is responsible for dispatching UDP events to linked Things.
*
* @author Lennert Coopman - Initial contribution
*/
@NonNullByDefault
public class YamahaMusiccastBridgeHandler extends BaseBridgeHandler {
private Gson gson = new Gson();
private final Logger logger = LoggerFactory.getLogger(YamahaMusiccastBridgeHandler.class);
private String threadname = getThing().getUID().getAsString();
private @Nullable ExecutorService executor;
private @Nullable Future<?> eventListenerJob;
private static final int UDP_PORT = 41100;
private static final int SOCKET_TIMEOUT_MILLISECONDS = 3000;
private static final int BUFFER_SIZE = 5120;
private @Nullable DatagramSocket socket;
private void receivePackets() {
try {
DatagramSocket s = new DatagramSocket(null);
s.setSoTimeout(SOCKET_TIMEOUT_MILLISECONDS);
s.setReuseAddress(true);
InetSocketAddress address = new InetSocketAddress(UDP_PORT);
s.bind(address);
socket = s;
logger.trace("UDP Listener got socket on port {} with timeout {}", UDP_PORT, SOCKET_TIMEOUT_MILLISECONDS);
} catch (SocketException e) {
logger.trace("UDP Listener got SocketException: {}", e.getMessage(), e);
socket = null;
return;
}
DatagramPacket packet = new DatagramPacket(new byte[BUFFER_SIZE], BUFFER_SIZE);
DatagramSocket localSocket = socket;
while (localSocket != null) {
try {
localSocket.receive(packet);
String received = new String(packet.getData(), 0, packet.getLength());
String trackingID = UUID.randomUUID().toString().replace("-", "").substring(0, 32);
logger.trace("Received packet: {} (Tracking: {})", received, trackingID);
handleUDPEvent(received, trackingID);
} catch (SocketTimeoutException e) {
// Nothing to do on socket timeout
} catch (IOException e) {
logger.trace("UDP Listener got IOException waiting for datagram: {}", e.getMessage());
localSocket = null;
}
}
logger.trace("UDP Listener exiting");
}
public YamahaMusiccastBridgeHandler(Bridge bridge) {
super(bridge);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
@Override
public void initialize() {
updateStatus(ThingStatus.ONLINE);
executor = Executors.newSingleThreadExecutor(new NamedThreadFactory(threadname));
Future<?> localEventListenerJob = eventListenerJob;
ExecutorService localExecutor = executor;
if (localEventListenerJob == null || localEventListenerJob.isCancelled()) {
if (localExecutor != null) {
localEventListenerJob = localExecutor.submit(this::receivePackets);
}
}
}
@Override
public void dispose() {
super.dispose();
Future<?> localEventListenerJob = eventListenerJob;
ExecutorService localExecutor = executor;
if (localEventListenerJob != null) {
localEventListenerJob.cancel(true);
localEventListenerJob = null;
}
if (localExecutor != null) {
localExecutor.shutdownNow();
localExecutor = null;
}
}
public void handleUDPEvent(String json, String trackingID) {
String udpDeviceId = "";
Bridge bridge = (Bridge) thing;
for (Thing thing : bridge.getThings()) {
ThingStatusInfo statusInfo = thing.getStatusInfo();
switch (statusInfo.getStatus()) {
case ONLINE:
logger.trace("Thing Status: ONLINE - {}", thing.getLabel());
YamahaMusiccastHandler handler = (YamahaMusiccastHandler) thing.getHandler();
if (handler != null) {
logger.trace("UDP: {} - {} ({} - Tracking: {})", json, handler.getDeviceId(), thing.getLabel(),
trackingID);
@Nullable
UdpMessage targetObject = gson.fromJson(json, UdpMessage.class);
if (targetObject != null) {
udpDeviceId = targetObject.getDeviceId();
if (udpDeviceId.equals(handler.getDeviceId())) {
handler.processUDPEvent(json, trackingID);
}
}
}
break;
default:
logger.trace("Thing Status: NOT ONLINE - {} (Tracking: {})", thing.getLabel(), trackingID);
break;
}
}
}
}

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) 2010-2021 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.yamahamusiccast.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link YamahaMusiccastConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Lennert Coopman - Initial contribution
*/
@NonNullByDefault
public class YamahaMusiccastConfiguration {
public @Nullable String host;
public @Nullable Boolean syncVolume;
public @Nullable String defaultAfterMCLink;
}

View File

@ -0,0 +1,67 @@
/**
* Copyright (c) 2010-2021 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.yamahamusiccast.internal;
import static org.openhab.binding.yamahamusiccast.internal.YamahaMusiccastBindingConstants.*;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
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;
/**
* The {@link YamahamusiccastHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Lennert Coopman - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.yamahamusiccast", service = ThingHandlerFactory.class)
public class YamahaMusiccastHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set
.of(YamahaMusiccastBindingConstants.THING_DEVICE, YamahaMusiccastBindingConstants.THING_TYPE_BRIDGE);
private final YamahaMusiccastStateDescriptionProvider stateDescriptionProvider;
@Activate
public YamahaMusiccastHandlerFactory(@Reference YamahaMusiccastStateDescriptionProvider stateDescriptionProvider) {
this.stateDescriptionProvider = stateDescriptionProvider;
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(THING_TYPE_BRIDGE)) {
return new YamahaMusiccastBridgeHandler((Bridge) thing);
} else if (THING_DEVICE.equals(thingTypeUID)) {
return new YamahaMusiccastHandler(thing, stateDescriptionProvider);
}
return null;
}
}

View File

@ -0,0 +1,41 @@
/**
* Copyright (c) 2010-2021 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.yamahamusiccast.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link YamahaMusiccastStateDescriptionProvider} is responsible for handling the state options of a channel.
*
* @author Lennert Coopman - Initial contribution
*/
@Component(service = { DynamicStateDescriptionProvider.class, YamahaMusiccastStateDescriptionProvider.class })
@NonNullByDefault
public class YamahaMusiccastStateDescriptionProvider extends BaseDynamicStateDescriptionProvider {
@Reference
protected void setChannelTypeI18nLocalizationService(
final ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
}
protected void unsetChannelTypeI18nLocalizationService(
final ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
this.channelTypeI18nLocalizationService = null;
}
}

View File

@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2021 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.yamahamusiccast.internal.dto;
import com.google.gson.annotations.SerializedName;
/**
* This class represents the DeviceInfo request requested from the Yamaha model/device via the API.
*
* @author Lennert Coopman - Initial contribution
*/
public class DeviceInfo {
@SerializedName("response_code")
private String responseCode;
@SerializedName("model_name")
private String modelName;
@SerializedName("device_id")
private String deviceId;
public String getResponseCode() {
if (responseCode == null) {
responseCode = "";
}
return responseCode;
}
public String getModelName() {
if (modelName == null) {
modelName = "";
}
return modelName;
}
public String getDeviceId() {
if (deviceId == null) {
deviceId = "";
}
return deviceId;
}
}

View File

@ -0,0 +1,83 @@
/**
* Copyright (c) 2010-2021 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.yamahamusiccast.internal.dto;
import com.google.gson.JsonArray;
import com.google.gson.annotations.SerializedName;
/**
* This class represents the DistributionInfo request requested from the Yamaha model/device via the API.
*
* @author Lennert Coopman - Initial contribution
*/
public class DistributionInfo {
@SerializedName("response_code")
private String responseCode;
@SerializedName("group_id")
private String groupId;
@SerializedName("role")
private String role;
@SerializedName("server_zone")
private String serverZone;
@SerializedName("client_list")
private JsonArray clientList;
public String getResponseCode() {
if (responseCode == null) {
responseCode = "";
}
return responseCode;
}
public String getGroupId() {
if (groupId == null) {
groupId = "";
}
return groupId;
}
public String getRole() {
if (role == null) {
role = "";
}
return role;
}
public String getServerZone() {
if (serverZone == null) {
serverZone = "";
}
return serverZone;
}
public JsonArray getClientList() {
return clientList;
}
public class ClientList {
@SerializedName("ip_address")
private String ipaddress;
public String getIpaddress() {
if (ipaddress == null) {
ipaddress = "";
}
return ipaddress;
}
}
}

View File

@ -0,0 +1,50 @@
/**
* Copyright (c) 2010-2021 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.yamahamusiccast.internal.dto;
import com.google.gson.annotations.SerializedName;
/**
* This class represents the Features request requested from the Yamaha model/device via the API.
*
* @author Lennert Coopman - Initial contribution
*/
public class Features {
@SerializedName("response_code")
private String responseCode;
public String getResponseCode() {
if (responseCode == null) {
responseCode = "";
}
return responseCode;
}
@SerializedName("system")
private System system;
public System getSystem() {
return system;
}
public class System {
@SerializedName("zone_num")
private int zoneNum = 0;
public int getZoneNum() {
return zoneNum;
}
}
}

View File

@ -0,0 +1,118 @@
/**
* Copyright (c) 2010-2021 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.yamahamusiccast.internal.dto;
import com.google.gson.annotations.SerializedName;
/**
* This class represents the PlayInfo request requested from the Yamaha model/device via the API.
*
* @author Lennert Coopman - Initial contribution
*/
public class PlayInfo {
@SerializedName("response_code")
private String responseCode;
@SerializedName("playback")
private String playback;
@SerializedName("artist")
private String artist;
@SerializedName("track")
private String track;
@SerializedName("album")
private String album;
@SerializedName("albumart_url")
private String albumarturl;
@SerializedName("repeat")
private String repeat;
@SerializedName("shuffle")
private String shuffle;
@SerializedName("play_time")
private int playTime = 0;
@SerializedName("total_time")
private int totalTime = 0;
public String getResponseCode() {
if (responseCode == null) {
responseCode = "";
}
return responseCode;
}
public String getPlayback() {
if (playback == null) {
playback = "";
}
return playback;
}
public String getArtist() {
if (artist == null) {
artist = "";
}
return artist;
}
public String getTrack() {
if (track == null) {
track = "";
}
return track;
}
public String getAlbum() {
if (album == null) {
album = "";
}
return album;
}
public String getAlbumArtUrl() {
if (albumarturl == null) {
albumarturl = "";
}
return albumarturl;
}
public String getRepeat() {
if (repeat == null) {
repeat = "";
}
return repeat;
}
public String getShuffle() {
if (shuffle == null) {
shuffle = "";
}
return shuffle;
}
public int getPlayTime() {
return playTime;
}
public int getTotalTime() {
return totalTime;
}
}

View File

@ -0,0 +1,41 @@
/**
* Copyright (c) 2010-2021 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.yamahamusiccast.internal.dto;
import com.google.gson.JsonArray;
import com.google.gson.annotations.SerializedName;
/**
* This class represents the PresetInfo request requested from the Yamaha model/device via the API.
*
* @author Lennert Coopman - Initial contribution
*/
public class PresetInfo {
@SerializedName("response_code")
private String responseCode;
@SerializedName("preset_info")
private JsonArray presetInfo;
public String getResponseCode() {
if (responseCode == null) {
responseCode = "";
}
return responseCode;
}
public JsonArray getPresetInfo() {
return presetInfo;
}
}

View File

@ -0,0 +1,41 @@
/**
* Copyright (c) 2010-2021 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.yamahamusiccast.internal.dto;
import com.google.gson.JsonArray;
import com.google.gson.annotations.SerializedName;
/**
* This class represents the RecentInfo request requested from the Yamaha model/device via the API.
*
* @author Lennert Coopman - Initial contribution
*/
public class RecentInfo {
@SerializedName("response_code")
private String responseCode;
@SerializedName("recent_info")
private JsonArray recentInfo;
public String getResponseCode() {
if (responseCode == null) {
responseCode = "";
}
return responseCode;
}
public JsonArray getRecentInfo() {
return recentInfo;
}
}

View File

@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2021 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.yamahamusiccast.internal.dto;
import com.google.gson.annotations.SerializedName;
/**
* This class represents the response received from the Yamaha model/device via the API.
*
* @author Lennert Coopman - Initial contribution
*/
public class Response {
@SerializedName("response_code")
private String responseCode;
public String getResponseCode() {
if (responseCode == null) {
responseCode = "";
}
return responseCode;
}
}

View File

@ -0,0 +1,99 @@
/**
* Copyright (c) 2010-2021 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.yamahamusiccast.internal.dto;
import com.google.gson.annotations.SerializedName;
/**
* This class represents the Status request requested from the Yamaha model/device via the API.
*
* @author Lennert Coopman - Initial contribution
*/
public class Status {
@SerializedName("response_code")
private String responseCode;
@SerializedName("power")
private String power;
@SerializedName("mute")
private String mute;
@SerializedName("volume")
private int volume;
@SerializedName("max_volume")
private int maxVolume = 1;
@SerializedName("input")
private String input;
@SerializedName("sound_program")
private String soundProgram;
@SerializedName("sleep")
private int sleep = 0;
public String getResponseCode() {
if (responseCode == null) {
responseCode = "";
}
return responseCode;
}
public String getPower() {
if (power == null) {
power = "";
}
return power;
}
public String getMute() {
if (mute == null) {
mute = "";
}
return mute;
}
public int getVolume() {
return volume;
}
public int getMaxVolume() {
// if no value is returned, set to 1 to avoid division by zero
if (maxVolume == 0) {
maxVolume = 1;
}
return maxVolume;
}
public String getInput() {
if (input == null) {
input = "";
}
return input;
}
public String getSoundProgram() {
if (soundProgram == null) {
soundProgram = "";
}
return soundProgram;
}
public int getSleep() {
return sleep;
}
}

View File

@ -0,0 +1,179 @@
/**
* Copyright (c) 2010-2021 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.yamahamusiccast.internal.dto;
import com.google.gson.annotations.SerializedName;
/**
* This class represents the UDP event received from the Yamaha model/device.
*
* @author Lennert Coopman - Initial contribution
*/
public class UdpMessage {
@SerializedName("device_id")
private String deviceId;
public String getDeviceId() {
if (deviceId == null) {
deviceId = "";
}
return deviceId;
}
@SerializedName("main")
private Zone main;
@SerializedName("zone2")
private Zone zone2;
@SerializedName("zone3")
private Zone zone3;
@SerializedName("zone4")
private Zone zone4;
@SerializedName("netusb")
private NetUSB netusb;
@SerializedName("dist")
private Dist dist;
public Zone getMain() {
return main;
}
public Zone getZone2() {
return zone2;
}
public Zone getZone3() {
return zone3;
}
public Zone getZone4() {
return zone4;
}
public NetUSB getNetUSB() {
return netusb;
}
public Dist getDist() {
return dist;
}
public class Zone {
@SerializedName("power")
private String power;
@SerializedName("volume")
private int volume = 0;
@SerializedName("mute")
private String mute;
@SerializedName("input")
private String input;
@SerializedName("status_updated")
private String statusUpdated;
public String getPower() {
if (power == null) {
power = "";
}
return power;
}
public String getMute() {
if (mute == null) {
mute = "";
}
return mute;
}
public String getInput() {
if (input == null) {
input = "";
}
return input;
}
public int getVolume() {
return volume;
}
public String getstatusUpdated() {
if (statusUpdated == null) {
statusUpdated = "";
}
return statusUpdated;
}
}
public class NetUSB {
@SerializedName("preset_control")
private PresetControl presetControl;
@SerializedName("play_info_updated")
private String playInfoUpdated;
@SerializedName("play_time")
private int playTime;
public PresetControl getPresetControl() {
return presetControl;
}
public String getPlayInfoUpdated() {
if (playInfoUpdated == null) {
playInfoUpdated = "";
}
return playInfoUpdated;
}
public int getPlayTime() {
return playTime;
}
}
public class PresetControl {
@SerializedName("type")
private String type;
@SerializedName("num")
private int num = 1;
@SerializedName("result")
private String result;
public String getType() {
if (type == null) {
type = "";
}
return type;
}
public String getResult() {
if (result == null) {
result = "";
}
return result;
}
public int getNum() {
return num;
}
}
public class Dist {
@SerializedName("dist_info_updated")
private String distInfoUpdated;
public String getDistInfoUpdated() {
if (distInfoUpdated == null) {
distInfoUpdated = "";
}
return distInfoUpdated;
}
}
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="yamahamusiccast" 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>Yamaha Musiccast Binding</name>
<description>This is the binding for Yamaha Musiccast</description>
</binding:binding>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="yamahamusiccast"
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">
<!-- Bridge -->
<bridge-type id="bridge">
<label>Virtual Bridge</label>
<description>Virtual Bridge to receive updates</description>
</bridge-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,193 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="yamahamusiccast"
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">
<thing-type id="device">
<supported-bridge-type-refs>
<bridge-type-ref id="bridge"/>
</supported-bridge-type-refs>
<label>Yamaha MusicCast Model</label>
<description>Your Yamaha model with MusicCast functionality</description>
<channel-groups>
<channel-group id="main" typeId="mainControls"/>
<channel-group id="zone2" typeId="zone2Controls"/>
<channel-group id="zone3" typeId="zone3Controls"/>
<channel-group id="zone4" typeId="zone4Controls"/>
<channel-group id="playerControls" typeId="playerControls"/>
</channel-groups>
<config-description>
<parameter name="host" type="text" required="true">
<label>Address</label>
<context>network-address</context>
<description>The IP address of the AVR to control.</description>
</parameter>
<parameter name="syncVolume" type="boolean">
<label>Sync Volume</label>
<description>Sync Volume across linked Music Cast models</description>
<default>true</default>
</parameter>
<parameter name="defaultAfterMCLink" type="text">
<label>MC Link Default Value</label>
<description>Default value for client when MC Link is broken</description>
<default>net_radio</default>
<options>
<option value="none">None</option>
<option value="net_radio">Net Radio</option>
</options>
</parameter>
</config-description>
</thing-type>
<channel-group-type id="mainControls">
<label>Main Zone (default)</label>
</channel-group-type>
<channel-group-type id="zone2Controls">
<label>Zone 2</label>
</channel-group-type>
<channel-group-type id="zone3Controls">
<label>Zone 3</label>
</channel-group-type>
<channel-group-type id="zone4Controls">
<label>Zone 4</label>
</channel-group-type>
<channel-group-type id="playerControls">
<label>Player Controls</label>
<channels>
<channel id="player" typeId="player"/>
<channel id="artist" typeId="artist"/>
<channel id="track" typeId="track"/>
<channel id="album" typeId="album"/>
<channel id="albumArt" typeId="albumArt"/>
<channel id="repeat" typeId="repeat"/>
<channel id="shuffle" typeId="shuffle"/>
<channel id="playTime" typeId="playTime"/>
<channel id="totalTime" typeId="totalTime"/>
</channels>
</channel-group-type>
<channel-type id="volumeAbs">
<item-type>Number</item-type>
<label>Volume</label>
<description>Volume channel - Absolute value</description>
</channel-type>
<channel-type id="input">
<item-type>String</item-type>
<label>Input</label>
<description>Input channel</description>
</channel-type>
<channel-type id="soundProgram">
<item-type>String</item-type>
<label>Sound Program</label>
<description>SoundProgram channel</description>
</channel-type>
<channel-type id="selectPreset">
<item-type>String</item-type>
<label>NetRadio/USB Preset</label>
<description>Select Net Radio/USB Preset channel</description>
</channel-type>
<channel-type id="player">
<item-type>Player</item-type>
<label>NetRadio/USB Player</label>
<description>Player for Net Radio/USB channel</description>
</channel-type>
<channel-type id="sleep">
<item-type>Number</item-type>
<label>Sleep</label>
<description>Sleep Time in minutes</description>
<state>
<options>
<option value="0">No Sleep</option>
<option value="30">30 min</option>
<option value="60">60 min</option>
<option value="90">90 min</option>
<option value="120">120 min</option>
</options>
</state>
</channel-type>
<channel-type id="recallScene">
<item-type>Number</item-type>
<label>Scene Selection</label>
<description>Scene selection (if available)</description>
<state>
<options>
<option value="1">Scene 1</option>
<option value="2">Scene 2</option>
<option value="3">Scene 3</option>
<option value="4">Scene 4</option>
<option value="5">Scene 5</option>
<option value="6">Scene 6</option>
<option value="7">Scene 7</option>
<option value="8">Scene 8</option>
</options>
</state>
</channel-type>
<channel-type id="artist">
<item-type>String</item-type>
<label>Artist</label>
<description>Artist</description>
</channel-type>
<channel-type id="track">
<item-type>String</item-type>
<label>Track</label>
<description>Track</description>
</channel-type>
<channel-type id="album">
<item-type>String</item-type>
<label>Album</label>
<description>Album</description>
</channel-type>
<channel-type id="albumArt">
<item-type>Image</item-type>
<label>Album Art</label>
<description>Album Art</description>
</channel-type>
<channel-type id="repeat">
<item-type>String</item-type>
<label>Repeat</label>
<description>Repeat mode</description>
<state>
<options>
<option value="off">Off</option>
<option value="one">One</option>
<option value="all">All</option>
</options>
</state>
</channel-type>
<channel-type id="shuffle">
<item-type>String</item-type>
<label>Shuffle</label>
<description>Shuffle mode</description>
<state>
<options>
<option value="off">Off</option>
<option value="on">On</option>
<option value="songs">Songs</option>
<option value="albums">Albums</option>
</options>
</state>
</channel-type>
<channel-type id="mclinkStatus">
<item-type>String</item-type>
<label>Status MusicCast</label>
<description>MusicCast Status</description>
</channel-type>
<channel-type id="playTime">
<item-type>Number:Time</item-type>
<label>Play Time</label>
<description>Play Time</description>
</channel-type>
<channel-type id="totalTime">
<item-type>String</item-type>
<label>Total Time</label>
<description>Total Time</description>
</channel-type>
</thing:thing-descriptions>

View File

@ -373,6 +373,7 @@
<module>org.openhab.binding.wolfsmartset</module> <module>org.openhab.binding.wolfsmartset</module>
<module>org.openhab.binding.xmltv</module> <module>org.openhab.binding.xmltv</module>
<module>org.openhab.binding.xmppclient</module> <module>org.openhab.binding.xmppclient</module>
<module>org.openhab.binding.yamahamusiccast</module>
<module>org.openhab.binding.yamahareceiver</module> <module>org.openhab.binding.yamahareceiver</module>
<module>org.openhab.binding.yioremote</module> <module>org.openhab.binding.yioremote</module>
<module>org.openhab.binding.yeelight</module> <module>org.openhab.binding.yeelight</module>