added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.sonyaudio</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,99 @@
# SonyAudio Binding
This binding integrates the [Sony Audio Control API](https://developer.sony.com/develop/audio-control-api/).
## Supported Things
For the moment the devices that are supported by this binding are
* STR-DN1080
* HT-CT800
* SRS-ZR5
* HT-ST5000
* HT-Z9F
* HT-ZF9
* HT-MT500
When being defined in a \*.things file, the specific thing types
STR-DN1080, HT-ST5000, HT-ZF9, HT-Z9F, HT-CT800, HT-MT500 and SRS-ZR5 should be used.
Please note that these thing types are case sensitive (you need to define them in upper case).
## Discovery
The SonyAudio devices are discovered through UPnP in the local network and all devices are put in the Inbox.
## Thing Configuration
The SonyAudio Thing requires the network address, port and path as a configuration value in order for the binding to know how to access the device.
Additionally, a refresh interval, used to poll the Sony Audio device, can be specified (in seconds).
```
Thing sonyaudio:HT-ST5000:1 [ipAddress="192.168.123.123", port=10000, path="/sony", refresh=60]
```
## Channels
The devices support the following channels:
| Channel Type ID | Item Type | Access Mode | Description | Thing types |
|----------------------------|-----------|-------------|---------------------------------------------------------------------------------------|--------------------------------------------------------|
| power | Switch | RW | Main power on/off | HT-CT800, SRS-ZR5, HT-ST5000, HT-ZF9, HT-Z9F, HT-MT500 |
| input | String | RW | Set or get the input source | HT-CT800, SRS-ZR5, HT-ST5000, HT-ZF9, HT-Z9F, HT-MT500 |
| volume | Dimmer | RW | Set or get the master volume | HT-CT800, SRS-ZR5, HT-ST5000, HT-ZF9, HT-Z9F, HT-MT500 |
| mute | Switch | RW | Set or get the mute state of the master volume | HT-CT800, SRS-ZR5, HT-ST5000, HT-ZF9, HT-Z9F, HT-MT500 |
| soundField | String | RW | Sound field | HT-CT800, SRS-ZR5, HT-ST5000, HT-ZF9, HT-Z9F, HT-MT500 |
| master#power | Switch | RW | Main power on/off | STR-1080 |
| master#soundField | String | RW | Sound field | STR-1080 |
| zone1#power | Switch | RW | Power for zone1 for devices supporting multizone | STR-1080 |
| zone1#input | String | RW | Set or get the input source for zone1 for devices supporting multizone | STR-1080 |
| zone1#volume | Dimmer | RW | Set or get the zone1 volume for devices supporting multizone | STR-1080 |
| zone1#mute | Switch | RW | Set or get the mute state for zone1 volume | STR-1080 |
| zone2#power | Switch | RW | Power for zone2 for devices supporting multizone | STR-1080 |
| zone2#input | String | RW | Set or get the input source for zone2 for devices supporting multizone | STR-1080 |
| zone2#volume | Dimmer | RW | Set or get the zone2 volume for devices supporting multizone | STR-1080 |
| zone2#mute | Switch | RW | Set or get the mute state for zone2 volume | STR-1080 |
| zone3#power | Switch | RW | Power for zone3 for devices supporting multizone | none |
| zone3#input | String | RW | Set or get the input source for zone3 for devices supporting multizone | none |
| zone3#volume | Dimmer | RW | Set or get the zone3 volume for devices supporting multizone | none |
| zone3#mute | Switch | RW | Set or get the mute state for zone3 volume | none |
| zone4#power | Switch | RW | Power for zone4 for devices supporting multizone | STR-1080 |
| zone4#input | String | RW | Set or get the input source for zone4 for devices supporting multizone | STR-1080 |
| zone4#volume | Dimmer | RW | Set or get the zone4 volume for devices supporting multizone | STR-1080 |
| zone4#mute | Switch | RW | Set or get the mute state for zone4 volume | STR-1080 |
| radio#broadcastFreq | Number | R | Current radio frequency | STR-1080 |
| radio#broadcastStation | Number | RW | Set or get current preset radio station | STR-1080 |
| radio#broadcastSeekStation | String | W | Seek for new broadcast station, forward search "fwdSeeking" and backward "bwdSeeking" | STR-1080 |
| nightMode | Switch | RW | Set or get the Night Mode state | HT-ZF9 |
## Full Example
demo.things:
```
Thing sonyaudio:HT-ST5000:living [ipAddress="192.168.123.123"]
```
demo.items:
```
Group SonyAudio <sonyaudio>
Dimmer Sony_Volume "Volume [%.0f %%]" <soundvolume> (SonyAudio) {channel="sonyaudio:HT-ST5000:living:volume"}
Switch Sony_Mute "Mute" <soundvolume_mute> (SonyAudio) {channel="sonyaudio:HT-ST5000:living:mute"}
String Sony_Sound_Field "Sound Field: [%s]" <text> (SonyAudio) {channel="sonyaudio:HT-ST5000:living:master#soundField"}
```
demo.sitemap:
```
sitemap demo label="Main Menu" {
Frame label="Sony" {
Text label="Volume" icon="soundvolume" {
Slider item=Sony_Volume
Switch item=Sony_Mute
}
Text item=Sony_Sound_Field
}
}
```

View File

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

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.sonyaudio-${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-sonyaudio" description="Sony Audio Binding" version="${project.version}">
<feature>openhab-transport-upnp</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.sonyaudio/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,98 @@
/**
* 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.sonyaudio.internal;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link SonyAudioBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author David - Initial contribution
*/
public class SonyAudioBindingConstants {
private static final String BINDING_ID = "sonyaudio";
public static final String SONY_TYPE_STRDN1080 = "STR-DN1080";
public static final String SONY_TYPE_HTCT800 = "HT-CT800";
public static final String SONY_TYPE_HTST5000 = "HT-ST5000";
public static final String SONY_TYPE_HTZ9F = "HT-Z9F";
public static final String SONY_TYPE_HTZF9 = "HT-ZF9";
public static final String SONY_TYPE_HTMT500 = "HT-MT500";
public static final String SONY_TYPE_SRSZR5 = "SRS-ZR5";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_STRDN1080 = new ThingTypeUID(BINDING_ID, SONY_TYPE_STRDN1080);
public static final ThingTypeUID THING_TYPE_HTCT800 = new ThingTypeUID(BINDING_ID, SONY_TYPE_HTCT800);
public static final ThingTypeUID THING_TYPE_HTST5000 = new ThingTypeUID(BINDING_ID, SONY_TYPE_HTST5000);
public static final ThingTypeUID THING_TYPE_HTZ9F = new ThingTypeUID(BINDING_ID, SONY_TYPE_HTZ9F);
public static final ThingTypeUID THING_TYPE_HTZF9 = new ThingTypeUID(BINDING_ID, SONY_TYPE_HTZF9);
public static final ThingTypeUID THING_TYPE_HTMT500 = new ThingTypeUID(BINDING_ID, SONY_TYPE_HTMT500);
public static final ThingTypeUID THING_TYPE_SRSZR5 = new ThingTypeUID(BINDING_ID, SONY_TYPE_SRSZR5);
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream
.of(THING_TYPE_STRDN1080, THING_TYPE_HTCT800, THING_TYPE_HTST5000, THING_TYPE_HTZ9F, THING_TYPE_HTZF9,
THING_TYPE_HTMT500, THING_TYPE_SRSZR5)
.collect(Collectors.toSet());
// List of thing parameters names
public static final String HOST_PARAMETER = "ipAddress";
public static final String SCALAR_PORT_PARAMETER = "port";
public static final String SCALAR_PATH_PARAMETER = "path";
public static final String REFRESHINTERVAL = "refreshInterval";
// List of all Channel ids
public static final String CHANNEL_POWER = "power";
public static final String CHANNEL_INPUT = "input";
public static final String CHANNEL_VOLUME = "volume";
public static final String CHANNEL_MUTE = "mute";
public static final String CHANNEL_SOUND_FIELD = "soundField";
public static final String CHANNEL_MASTER_POWER = "master#power";
public static final String CHANNEL_MASTER_SOUND_FIELD = "master#soundField";
public static final String CHANNEL_ZONE1_POWER = "zone1#power";
public static final String CHANNEL_ZONE1_INPUT = "zone1#input";
public static final String CHANNEL_ZONE1_VOLUME = "zone1#volume";
public static final String CHANNEL_ZONE1_MUTE = "zone1#mute";
public static final String CHANNEL_ZONE2_POWER = "zone2#power";
public static final String CHANNEL_ZONE2_INPUT = "zone2#input";
public static final String CHANNEL_ZONE2_VOLUME = "zone2#volume";
public static final String CHANNEL_ZONE2_MUTE = "zone2#mute";
public static final String CHANNEL_ZONE3_POWER = "zone3#power";
public static final String CHANNEL_ZONE3_INPUT = "zone3#input";
public static final String CHANNEL_ZONE3_VOLUME = "zone3#volume";
public static final String CHANNEL_ZONE3_MUTE = "zone3#mute";
public static final String CHANNEL_ZONE4_POWER = "zone4#power";
public static final String CHANNEL_ZONE4_INPUT = "zone4#input";
public static final String CHANNEL_ZONE4_VOLUME = "zone4#volume";
public static final String CHANNEL_ZONE4_MUTE = "zone4#mute";
public static final String CHANNEL_RADIO_FREQ = "radio#broadcastFreq";
public static final String CHANNEL_RADIO_STATION = "radio#broadcastStation";
public static final String CHANNEL_RADIO_SEEK_STATION = "radio#broadcastSeekStation";
public static final String CHANNEL_NIGHTMODE = "nightMode";
// Used for Discovery service
public static final String MANUFACTURER = "SONY";
public static final String UPNP_DEVICE_TYPE = "MediaRenderer";
}

View File

@@ -0,0 +1,37 @@
/**
* 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.sonyaudio.internal;
import java.util.EventListener;
import org.openhab.binding.sonyaudio.internal.protocol.SonyAudioConnection;
/**
* The {@link SonyAudioEventListener} event listener interface
* handlers.
*
* @author David - Initial contribution
*/
public interface SonyAudioEventListener extends EventListener {
void updateConnectionState(boolean connected);
void updateInput(int zone, SonyAudioConnection.SonyAudioInput input);
void updateSeekStation(String seek);
void updateCurrentRadioStation(int radioStation);
void updateVolume(int zone, SonyAudioConnection.SonyAudioVolume volume);
void updatePowerStatus(int zone, boolean power);
}

View File

@@ -0,0 +1,80 @@
/**
* 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.sonyaudio.internal;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.openhab.binding.sonyaudio.internal.handler.HtCt800Handler;
import org.openhab.binding.sonyaudio.internal.handler.HtMt500Handler;
import org.openhab.binding.sonyaudio.internal.handler.HtSt5000Handler;
import org.openhab.binding.sonyaudio.internal.handler.HtZ9fHandler;
import org.openhab.binding.sonyaudio.internal.handler.HtZf9Handler;
import org.openhab.binding.sonyaudio.internal.handler.SrsZr5Handler;
import org.openhab.binding.sonyaudio.internal.handler.StrDn1080Handler;
import org.openhab.core.io.net.http.WebSocketFactory;
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.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link SonyAudioHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author David - Initial contribution
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.sonyaudio")
public class SonyAudioHandlerFactory extends BaseThingHandlerFactory {
private WebSocketClient webSocketClient;
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SonyAudioBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
switch (thingTypeUID.getId()) {
case SonyAudioBindingConstants.SONY_TYPE_STRDN1080:
return new StrDn1080Handler(thing, webSocketClient);
case SonyAudioBindingConstants.SONY_TYPE_HTCT800:
return new HtCt800Handler(thing, webSocketClient);
case SonyAudioBindingConstants.SONY_TYPE_HTST5000:
return new HtSt5000Handler(thing, webSocketClient);
case SonyAudioBindingConstants.SONY_TYPE_HTZ9F:
return new HtZ9fHandler(thing, webSocketClient);
case SonyAudioBindingConstants.SONY_TYPE_HTZF9:
return new HtZf9Handler(thing, webSocketClient);
case SonyAudioBindingConstants.SONY_TYPE_HTMT500:
return new HtMt500Handler(thing, webSocketClient);
case SonyAudioBindingConstants.SONY_TYPE_SRSZR5:
return new SrsZr5Handler(thing, webSocketClient);
default:
return null;
}
}
@Reference
protected void setWebSocketFactory(WebSocketFactory webSocketFactory) {
this.webSocketClient = webSocketFactory.getCommonWebSocketClient();
}
protected void unsetWebSocketFactory(WebSocketFactory webSocketFactory) {
this.webSocketClient = null;
}
}

View File

@@ -0,0 +1,147 @@
/**
* 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.sonyaudio.internal.discovery;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.jupnp.model.meta.RemoteDevice;
import org.openhab.binding.sonyaudio.internal.SonyAudioBindingConstants;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.upnp.UpnpDiscoveryParticipant;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class identifies SONY products by their Upnp service information.
*
* @author David Åberg - Initial contribution
*/
@Component(immediate = true)
public class SonyAudioDiscoveryParticipant implements UpnpDiscoveryParticipant {
private final Logger logger = LoggerFactory.getLogger(SonyAudioDiscoveryParticipant.class);
private Set<ThingTypeUID> supportedThingTypes;
public SonyAudioDiscoveryParticipant() {
this.supportedThingTypes = SonyAudioBindingConstants.SUPPORTED_THING_TYPES_UIDS;
}
@Override
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
return supportedThingTypes;
}
@Override
public DiscoveryResult createResult(RemoteDevice device) {
DiscoveryResult result = null;
ThingUID thingUid = getThingUID(device);
if (thingUid != null) {
String label = StringUtils.isEmpty(device.getDetails().getFriendlyName()) ? device.getDisplayString()
: device.getDetails().getFriendlyName();
String host = device.getIdentity().getDescriptorURL().getHost();
int port = device.getIdentity().getDescriptorURL().getPort();
String path = device.getIdentity().getDescriptorURL().getPath();
try {
Map<String, Object> properties = getDescription(host, port, path);
properties.put(SonyAudioBindingConstants.HOST_PARAMETER,
device.getIdentity().getDescriptorURL().getHost());
result = DiscoveryResultBuilder.create(thingUid).withLabel(label).withProperties(properties).build();
} catch (IOException e) {
return null;
}
}
return result;
}
@Override
public ThingUID getThingUID(RemoteDevice device) {
ThingUID result = null;
if (!StringUtils.containsIgnoreCase(device.getDetails().getManufacturerDetails().getManufacturer(),
SonyAudioBindingConstants.MANUFACTURER)) {
return result;
}
logger.debug("Manufacturer matched: search: {}, device value: {}.", SonyAudioBindingConstants.MANUFACTURER,
device.getDetails().getManufacturerDetails().getManufacturer());
if (!StringUtils.containsIgnoreCase(device.getType().getType(), SonyAudioBindingConstants.UPNP_DEVICE_TYPE)) {
return result;
}
logger.debug("Device type matched: search: {}, device value: {}.", SonyAudioBindingConstants.UPNP_DEVICE_TYPE,
device.getType().getType());
logger.debug("Device services: {}", device.getServices().toString());
String deviceModel = device.getDetails().getModelDetails() != null
? device.getDetails().getModelDetails().getModelName()
: null;
logger.debug("Device model: {}.", deviceModel);
ThingTypeUID thingTypeUID = findThingType(deviceModel);
if (thingTypeUID != null) {
result = new ThingUID(thingTypeUID, device.getIdentity().getUdn().getIdentifierString());
}
return result;
}
private ThingTypeUID findThingType(String deviceModel) {
ThingTypeUID thingTypeUID = null;
for (ThingTypeUID thingType : SonyAudioBindingConstants.SUPPORTED_THING_TYPES_UIDS) {
if (thingType.getId().equalsIgnoreCase(deviceModel)) {
return thingType;
}
}
return thingTypeUID;
}
private Map<String, Object> getDescription(String host, int port, String path) throws IOException {
Map<String, Object> properties = new HashMap<>(2, 1);
URL url = new URL("http", host, port, path);
logger.debug("URL: {}", url.toString());
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(url.openStream()))) {
String s;
StringBuilder builder = new StringBuilder();
while ((s = bufferedReader.readLine()) != null) {
builder.append(s);
}
Pattern ScalarWebAPImatch = Pattern.compile("<av:X_ScalarWebAPI_BaseURL>(.*)</av:X_ScalarWebAPI_BaseURL>");
Pattern baseURLmatch = Pattern.compile("http://(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):(\\d+)([^<]*)");
Matcher tagmatch = ScalarWebAPImatch.matcher(builder.toString());
if (tagmatch.find()) {
Matcher matcher = baseURLmatch.matcher(tagmatch.group());
matcher.find();
// String scalar_host = matcher.group(0);
int scalar_port = Integer.parseInt(matcher.group(2));
String scalar_path = matcher.group(3);
properties.put(SonyAudioBindingConstants.SCALAR_PORT_PARAMETER, scalar_port);
properties.put(SonyAudioBindingConstants.SCALAR_PATH_PARAMETER, scalar_path);
}
return properties;
}
}
}

View File

@@ -0,0 +1,89 @@
/**
* 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.sonyaudio.internal.handler;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
/**
* The {@link HtCt800Handler} is responsible for handling commands for HT-CT800, which are
* sent to one of the channels.
*
* @author David Åberg - Initial contribution
*/
public class HtCt800Handler extends SonyAudioHandler {
public HtCt800Handler(Thing thing, WebSocketClient webSocketClient) {
super(thing, webSocketClient);
}
@Override
public String setInputCommand(Command command) {
switch (command.toString().toLowerCase()) {
case "btaudio":
return "extInput:btAudio";
case "tv":
return "extInput:tv";
case "hdmi1":
return "extInput:hdmi?port=1";
case "hdmi2":
return "extInput:hdmi?port=2";
case "hdmi3":
return "extInput:hdmi?port=3";
case "analog":
return "extInput:line";
case "usb":
return "storage:usb1";
case "network":
return "dlna:music";
case "cast":
return "cast:audio";
}
return command.toString();
}
@Override
public StringType inputSource(String input) {
String in = input.toLowerCase();
if (in.contains("extinput:btaudio".toLowerCase())) {
return new StringType("btaudio");
}
if (in.contains("extinput:tv".toLowerCase())) {
return new StringType("tv");
}
if (in.contains("extinput:hdmi?port=1".toLowerCase())) {
return new StringType("hdmi1");
}
if (in.contains("extinput:hdmi?port=2".toLowerCase())) {
return new StringType("hdmi2");
}
if (in.contains("extinput:hdmi?port=3".toLowerCase())) {
return new StringType("hdmi3");
}
if (in.contains("extinput:line".toLowerCase())) {
return new StringType("analog");
}
if (in.contains("storage:usb1".toLowerCase())) {
return new StringType("usb");
}
if (in.contains("dlna:music".toLowerCase())) {
return new StringType("network");
}
if (in.contains("cast:audio".toLowerCase())) {
return new StringType("cast");
}
return new StringType(input);
}
}

View File

@@ -0,0 +1,74 @@
/**
* 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.sonyaudio.internal.handler;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
/**
* The {@link HtMt500Handler} is responsible for handling commands for HT-ST500, which are
* sent to one of the channels.
*
* @author David Åberg - Initial contribution
*/
public class HtMt500Handler extends SonyAudioHandler {
public HtMt500Handler(Thing thing, WebSocketClient webSocketClient) {
super(thing, webSocketClient);
}
@Override
public String setInputCommand(Command command) {
switch (command.toString().toLowerCase()) {
case "btaudio":
return "extInput:btAudio";
case "tv":
return "extInput:tv";
case "analog":
return "extInput:line";
case "usb":
return "storage:usb1";
case "network":
return "dlna:music";
case "cast":
return "cast:audio";
}
return command.toString();
}
@Override
public StringType inputSource(String input) {
String in = input.toLowerCase();
if (in.contains("extinput:btaudio".toLowerCase())) {
return new StringType("btaudio");
}
if (in.contains("extinput:tv".toLowerCase())) {
return new StringType("tv");
}
if (in.contains("extinput:line".toLowerCase())) {
return new StringType("analog");
}
if (in.contains("storage:usb1".toLowerCase())) {
return new StringType("usb");
}
if (in.contains("dlna:music".toLowerCase())) {
return new StringType("network");
}
if (in.contains("cast:audio".toLowerCase())) {
return new StringType("cast");
}
return new StringType(input);
}
}

View File

@@ -0,0 +1,89 @@
/**
* 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.sonyaudio.internal.handler;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
/**
* The {@link HtSt5000Handler} is responsible for handling commands for HT-ST5000, which are
* sent to one of the channels.
*
* @author David Åberg - Initial contribution
*/
public class HtSt5000Handler extends SonyAudioHandler {
public HtSt5000Handler(Thing thing, WebSocketClient webSocketClient) {
super(thing, webSocketClient);
}
@Override
public String setInputCommand(Command command) {
switch (command.toString().toLowerCase()) {
case "btaudio":
return "extInput:btAudio";
case "tv":
return "extInput:tv";
case "hdmi1":
return "extInput:hdmi?port=1";
case "hdmi2":
return "extInput:hdmi?port=2";
case "hdmi3":
return "extInput:hdmi?port=3";
case "analog":
return "extInput:line";
case "usb":
return "storage:usb1";
case "network":
return "dlna:music";
case "cast":
return "cast:audio";
}
return command.toString();
}
@Override
public StringType inputSource(String input) {
String in = input.toLowerCase();
if (in.contains("extinput:btaudio".toLowerCase())) {
return new StringType("btaudio");
}
if (in.contains("extinput:tv".toLowerCase())) {
return new StringType("tv");
}
if (in.contains("extinput:hdmi?port=1".toLowerCase())) {
return new StringType("hdmi1");
}
if (in.contains("extinput:hdmi?port=2".toLowerCase())) {
return new StringType("hdmi2");
}
if (in.contains("extinput:hdmi?port=3".toLowerCase())) {
return new StringType("hdmi3");
}
if (in.contains("extinput:line".toLowerCase())) {
return new StringType("analog");
}
if (in.contains("storage:usb1".toLowerCase())) {
return new StringType("usb");
}
if (in.contains("dlna:music".toLowerCase())) {
return new StringType("network");
}
if (in.contains("cast:audio".toLowerCase())) {
return new StringType("cast");
}
return new StringType(input);
}
}

View File

@@ -0,0 +1,84 @@
/**
* 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.sonyaudio.internal.handler;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
/**
* The {@link HtZ9fHandler} is responsible for handling commands for HT-Z9F, which are
* sent to one of the channels.
*
* @author David Åberg - Initial contribution
*/
public class HtZ9fHandler extends SonyAudioHandler {
public HtZ9fHandler(Thing thing, WebSocketClient webSocketClient) {
super(thing, webSocketClient);
}
@Override
public String setInputCommand(Command command) {
switch (command.toString().toLowerCase()) {
case "btaudio":
return "extInput:btAudio";
case "tv":
return "extInput:tv";
case "hdmi1":
return "extInput:hdmi?port=1";
case "hdmi2":
return "extInput:hdmi?port=2";
case "analog":
return "extInput:line";
case "usb":
return "storage:usb1";
case "network":
return "dlna:music";
case "cast":
return "cast:audio";
}
return command.toString();
}
@Override
public StringType inputSource(String input) {
String in = input.toLowerCase();
if (in.contains("extinput:btaudio".toLowerCase())) {
return new StringType("btaudio");
}
if (in.contains("extinput:tv".toLowerCase())) {
return new StringType("tv");
}
if (in.contains("extinput:hdmi?port=1".toLowerCase())) {
return new StringType("hdmi1");
}
if (in.contains("extinput:hdmi?port=2".toLowerCase())) {
return new StringType("hdmi2");
}
if (in.contains("extinput:line".toLowerCase())) {
return new StringType("analog");
}
if (in.contains("storage:usb1".toLowerCase())) {
return new StringType("usb");
}
if (in.contains("dlna:music".toLowerCase())) {
return new StringType("network");
}
if (in.contains("cast:audio".toLowerCase())) {
return new StringType("cast");
}
return new StringType(input);
}
}

View File

@@ -0,0 +1,84 @@
/**
* 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.sonyaudio.internal.handler;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
/**
* The {@link HtZf9Handler} is responsible for handling commands for HT-ZF9, which are
* sent to one of the channels.
*
* @author David Åberg - Initial contribution
*/
public class HtZf9Handler extends SonyAudioHandler {
public HtZf9Handler(Thing thing, WebSocketClient webSocketClient) {
super(thing, webSocketClient);
}
@Override
public String setInputCommand(Command command) {
switch (command.toString().toLowerCase()) {
case "btaudio":
return "extInput:btAudio";
case "tv":
return "extInput:tv";
case "hdmi1":
return "extInput:hdmi?port=1";
case "hdmi2":
return "extInput:hdmi?port=2";
case "analog":
return "extInput:line";
case "usb":
return "storage:usb1";
case "network":
return "dlna:music";
case "cast":
return "cast:audio";
}
return command.toString();
}
@Override
public StringType inputSource(String input) {
String in = input.toLowerCase();
if (in.contains("extinput:btaudio".toLowerCase())) {
return new StringType("btaudio");
}
if (in.contains("extinput:tv".toLowerCase())) {
return new StringType("tv");
}
if (in.contains("extinput:hdmi?port=1".toLowerCase())) {
return new StringType("hdmi1");
}
if (in.contains("extinput:hdmi?port=2".toLowerCase())) {
return new StringType("hdmi2");
}
if (in.contains("extinput:line".toLowerCase())) {
return new StringType("analog");
}
if (in.contains("storage:usb1".toLowerCase())) {
return new StringType("usb");
}
if (in.contains("dlna:music".toLowerCase())) {
return new StringType("network");
}
if (in.contains("cast:audio".toLowerCase())) {
return new StringType("cast");
}
return new StringType(input);
}
}

View File

@@ -0,0 +1,596 @@
/**
* 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.sonyaudio.internal.handler;
import static org.openhab.binding.sonyaudio.internal.SonyAudioBindingConstants.*;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.openhab.binding.sonyaudio.internal.SonyAudioBindingConstants;
import org.openhab.binding.sonyaudio.internal.SonyAudioEventListener;
import org.openhab.binding.sonyaudio.internal.protocol.SonyAudioConnection;
import org.openhab.core.cache.ExpiringCache;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StringType;
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.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SonyAudioHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author David Åberg - Initial contribution
*/
abstract class SonyAudioHandler extends BaseThingHandler implements SonyAudioEventListener {
private final Logger logger = LoggerFactory.getLogger(SonyAudioHandler.class);
private WebSocketClient webSocketClient;
protected SonyAudioConnection connection;
private ScheduledFuture<?> connectionCheckerFuture;
private ScheduledFuture<?> refreshJob;
private int currentRadioStation = 0;
private final Map<Integer, String> input_zone = new HashMap<>();
private static final long CACHE_EXPIRY = TimeUnit.SECONDS.toMillis(5);
protected ExpiringCache<Boolean>[] powerCache;
protected ExpiringCache<SonyAudioConnection.SonyAudioInput>[] inputCache;
protected ExpiringCache<SonyAudioConnection.SonyAudioVolume>[] volumeCache;
protected ExpiringCache<Map<String, String>> soundSettingsCache;
protected Supplier<Boolean>[] powerSupplier;
protected Supplier<SonyAudioConnection.SonyAudioInput>[] inputSupplier;
protected Supplier<SonyAudioConnection.SonyAudioVolume>[] volumeSupplier;
protected Supplier<Map<String, String>> soundSettingsSupplier;
@SuppressWarnings("unchecked")
public SonyAudioHandler(Thing thing, WebSocketClient webSocketClient) {
super(thing);
this.webSocketClient = webSocketClient;
powerCache = new ExpiringCache[5];
powerSupplier = new Supplier[5];
inputCache = new ExpiringCache[5];
inputSupplier = new Supplier[5];
volumeCache = new ExpiringCache[5];
volumeSupplier = new Supplier[5];
for (int i = 0; i < 5; i++) {
final int index = i;
inputSupplier[i] = () -> {
try {
return connection.getInput(index);
} catch (IOException ex) {
throw new CompletionException(ex);
}
};
powerSupplier[i] = () -> {
try {
return connection.getPower(index);
} catch (IOException ex) {
throw new CompletionException(ex);
}
};
volumeSupplier[i] = () -> {
try {
return connection.getVolume(index);
} catch (IOException ex) {
throw new CompletionException(ex);
}
};
powerCache[i] = new ExpiringCache<>(CACHE_EXPIRY, powerSupplier[i]);
inputCache[i] = new ExpiringCache<>(CACHE_EXPIRY, inputSupplier[i]);
volumeCache[i] = new ExpiringCache<>(CACHE_EXPIRY, volumeSupplier[i]);
}
soundSettingsSupplier = () -> {
try {
return connection.getSoundSettings();
} catch (IOException ex) {
throw new CompletionException(ex);
}
};
soundSettingsCache = new ExpiringCache<>(CACHE_EXPIRY, soundSettingsSupplier);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (connection == null || !connection.checkConnection()) {
logger.debug("Thing not yet initialized!");
return;
}
String id = channelUID.getId();
logger.debug("Handle command {} {}", channelUID, command);
if (getThing().getStatusInfo().getStatus() != ThingStatus.ONLINE) {
switch (id) {
case CHANNEL_POWER:
case CHANNEL_MASTER_POWER:
logger.debug("Device powered off sending {} {}", channelUID, command);
break;
default:
logger.debug("Device powered off ignore command {} {}", channelUID, command);
return;
}
}
try {
switch (id) {
case CHANNEL_POWER:
case CHANNEL_MASTER_POWER:
handlePowerCommand(command, channelUID);
break;
case CHANNEL_ZONE1_POWER:
handlePowerCommand(command, channelUID, 1);
break;
case CHANNEL_ZONE2_POWER:
handlePowerCommand(command, channelUID, 2);
break;
case CHANNEL_ZONE3_POWER:
handlePowerCommand(command, channelUID, 3);
break;
case CHANNEL_ZONE4_POWER:
handlePowerCommand(command, channelUID, 4);
break;
case CHANNEL_INPUT:
handleInputCommand(command, channelUID);
break;
case CHANNEL_ZONE1_INPUT:
handleInputCommand(command, channelUID, 1);
break;
case CHANNEL_ZONE2_INPUT:
handleInputCommand(command, channelUID, 2);
break;
case CHANNEL_ZONE3_INPUT:
handleInputCommand(command, channelUID, 3);
break;
case CHANNEL_ZONE4_INPUT:
handleInputCommand(command, channelUID, 4);
break;
case CHANNEL_VOLUME:
handleVolumeCommand(command, channelUID);
break;
case CHANNEL_ZONE1_VOLUME:
handleVolumeCommand(command, channelUID, 1);
break;
case CHANNEL_ZONE2_VOLUME:
handleVolumeCommand(command, channelUID, 2);
break;
case CHANNEL_ZONE3_VOLUME:
handleVolumeCommand(command, channelUID, 3);
break;
case CHANNEL_ZONE4_VOLUME:
handleVolumeCommand(command, channelUID, 4);
break;
case CHANNEL_MUTE:
handleMuteCommand(command, channelUID);
break;
case CHANNEL_ZONE1_MUTE:
handleMuteCommand(command, channelUID, 1);
break;
case CHANNEL_ZONE2_MUTE:
handleMuteCommand(command, channelUID, 2);
break;
case CHANNEL_ZONE3_MUTE:
handleMuteCommand(command, channelUID, 3);
break;
case CHANNEL_ZONE4_MUTE:
handleMuteCommand(command, channelUID, 4);
break;
case CHANNEL_MASTER_SOUND_FIELD:
case CHANNEL_SOUND_FIELD:
handleSoundSettings(command, channelUID);
break;
case CHANNEL_RADIO_FREQ:
handleRadioCommand(command, channelUID);
break;
case CHANNEL_RADIO_STATION:
handleRadioStationCommand(command, channelUID);
break;
case CHANNEL_RADIO_SEEK_STATION:
handleRadioSeekStationCommand(command, channelUID);
break;
case CHANNEL_NIGHTMODE:
handleNightMode(command, channelUID);
break;
default:
logger.error("Command {}, {} not supported by {}!", id, command, channelUID);
}
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}
public void handleSoundSettings(Command command, ChannelUID channelUID) throws IOException {
if (command instanceof RefreshType) {
logger.debug("handleSoundSettings RefreshType");
Map<String, String> result = soundSettingsCache.getValue();
if (result != null) {
updateState(channelUID, new StringType(result.get("soundField")));
}
}
if (command instanceof StringType) {
logger.debug("handleSoundSettings set {}", command);
connection.setSoundSettings("soundField", ((StringType) command).toString());
}
}
public void handleNightMode(Command command, ChannelUID channelUID) throws IOException {
if (command instanceof RefreshType) {
logger.debug("handleNightMode RefreshType");
Map<String, String> result = soundSettingsCache.getValue();
if (result != null) {
updateState(channelUID, new StringType(result.get("nightMode")));
}
}
if (command instanceof OnOffType) {
logger.debug("handleNightMode set {}", command);
connection.setSoundSettings("nightMode", ((OnOffType) command) == OnOffType.ON ? "on" : "off");
}
}
public void handlePowerCommand(Command command, ChannelUID channelUID) throws IOException {
handlePowerCommand(command, channelUID, 0);
}
public void handlePowerCommand(Command command, ChannelUID channelUID, int zone) throws IOException {
if (command instanceof RefreshType) {
try {
logger.debug("handlePowerCommand RefreshType {}", zone);
Boolean result = powerCache[zone].getValue();
updateState(channelUID, result ? OnOffType.ON : OnOffType.OFF);
} catch (CompletionException ex) {
throw new IOException(ex.getCause());
}
}
if (command instanceof OnOffType) {
logger.debug("handlePowerCommand set {} {}", zone, command);
connection.setPower(((OnOffType) command) == OnOffType.ON, zone);
}
}
public void handleInputCommand(Command command, ChannelUID channelUID) throws IOException {
handleInputCommand(command, channelUID, 0);
}
public void handleInputCommand(Command command, ChannelUID channelUID, int zone) throws IOException {
if (command instanceof RefreshType) {
logger.debug("handleInputCommand RefreshType {}", zone);
try {
SonyAudioConnection.SonyAudioInput result = inputCache[zone].getValue();
if (result != null) {
if (zone > 0) {
input_zone.put(zone, result.input);
}
updateState(channelUID, inputSource(result.input));
if (result.radio_freq.isPresent()) {
updateState(SonyAudioBindingConstants.CHANNEL_RADIO_FREQ,
new DecimalType(result.radio_freq.get() / 1000000.0));
}
}
} catch (CompletionException ex) {
throw new IOException(ex.getCause());
}
}
if (command instanceof StringType) {
logger.debug("handleInputCommand set {} {}", zone, command);
connection.setInput(setInputCommand(command), zone);
}
}
public void handleVolumeCommand(Command command, ChannelUID channelUID) throws IOException {
handleVolumeCommand(command, channelUID, 0);
}
public void handleVolumeCommand(Command command, ChannelUID channelUID, int zone) throws IOException {
if (command instanceof RefreshType) {
try {
logger.debug("handleVolumeCommand RefreshType {}", zone);
SonyAudioConnection.SonyAudioVolume result = volumeCache[zone].getValue();
if (result != null) {
updateState(channelUID, new PercentType(result.volume));
}
} catch (CompletionException ex) {
throw new IOException(ex.getCause());
}
}
if (command instanceof PercentType) {
logger.debug("handleVolumeCommand PercentType set {} {}", zone, command);
connection.setVolume(((PercentType) command).intValue(), zone);
}
if (command instanceof IncreaseDecreaseType) {
logger.debug("handleVolumeCommand IncreaseDecreaseType set {} {}", zone, command);
String change = command == IncreaseDecreaseType.INCREASE ? "+1" : "-1";
connection.setVolume(change, zone);
}
if (command instanceof OnOffType) {
logger.debug("handleVolumeCommand OnOffType set {} {}", zone, command);
connection.setMute(((OnOffType) command) == OnOffType.ON, zone);
}
}
public void handleMuteCommand(Command command, ChannelUID channelUID) throws IOException {
handleMuteCommand(command, channelUID, 0);
}
public void handleMuteCommand(Command command, ChannelUID channelUID, int zone) throws IOException {
if (command instanceof RefreshType) {
try {
logger.debug("handleMuteCommand RefreshType {}", zone);
SonyAudioConnection.SonyAudioVolume result = volumeCache[zone].getValue();
if (result != null) {
updateState(channelUID, result.mute ? OnOffType.ON : OnOffType.OFF);
}
} catch (CompletionException ex) {
throw new IOException(ex.getCause());
}
}
if (command instanceof OnOffType) {
logger.debug("handleMuteCommand set {} {}", zone, command);
connection.setMute(((OnOffType) command) == OnOffType.ON, zone);
}
}
public void handleRadioCommand(Command command, ChannelUID channelUID) throws IOException {
}
public void handleRadioStationCommand(Command command, ChannelUID channelUID) throws IOException {
if (command instanceof RefreshType) {
updateState(channelUID, new DecimalType(currentRadioStation));
}
if (command instanceof DecimalType) {
currentRadioStation = ((DecimalType) command).intValue();
String radioCommand = "radio:fm?contentId=" + currentRadioStation;
for (int i = 1; i <= 4; i++) {
String input = input_zone.get(i);
if (input != null && input.startsWith("radio:fm")) {
connection.setInput(radioCommand, i);
}
}
}
}
public void handleRadioSeekStationCommand(Command command, ChannelUID channelUID) throws IOException {
if (command instanceof RefreshType) {
updateState(channelUID, new StringType(""));
}
if (command instanceof StringType) {
switch (((StringType) command).toString()) {
case "fwdSeeking":
connection.radioSeekFwd();
break;
case "bwdSeeking":
connection.radioSeekBwd();
break;
}
}
}
public abstract String setInputCommand(Command command);
@Override
public void initialize() {
Configuration config = getThing().getConfiguration();
String ipAddress = (String) config.get(SonyAudioBindingConstants.HOST_PARAMETER);
String path = (String) config.get(SonyAudioBindingConstants.SCALAR_PATH_PARAMETER);
Object port_o = config.get(SonyAudioBindingConstants.SCALAR_PORT_PARAMETER);
int port = 10000;
if (port_o instanceof BigDecimal) {
port = ((BigDecimal) port_o).intValue();
} else if (port_o instanceof Integer) {
port = (int) port_o;
}
Object refresh_o = config.get(SonyAudioBindingConstants.REFRESHINTERVAL);
int refresh = 0;
if (refresh_o instanceof BigDecimal) {
refresh = ((BigDecimal) refresh_o).intValue();
} else if (refresh_o instanceof Integer) {
refresh = (int) refresh_o;
}
try {
connection = new SonyAudioConnection(ipAddress, port, path, this, scheduler, webSocketClient);
Runnable connectionChecker = () -> {
try {
if (!connection.checkConnection()) {
if (getThing().getStatus() != ThingStatus.OFFLINE) {
logger.debug("Lost connection");
updateStatus(ThingStatus.OFFLINE);
}
}
} catch (Exception ex) {
logger.warn("Exception in check connection to @{}. Cause: {}", connection.getConnectionName(),
ex.getMessage(), ex);
}
};
connectionCheckerFuture = scheduler.scheduleWithFixedDelay(connectionChecker, 1, 10, TimeUnit.SECONDS);
// Start the status updater
startAutomaticRefresh(refresh);
} catch (URISyntaxException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
}
}
@Override
public void dispose() {
logger.debug("Disposing SonyAudioHandler");
super.dispose();
if (connectionCheckerFuture != null) {
connectionCheckerFuture.cancel(true);
connectionCheckerFuture = null;
}
if (refreshJob != null) {
refreshJob.cancel(true);
refreshJob = null;
}
if (connection != null) {
connection.close();
connection = null;
}
}
@Override
public void updateConnectionState(boolean connected) {
logger.debug("Changing connection status to {}", connected);
if (connected) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE);
}
}
@Override
public void updateInput(int zone, SonyAudioConnection.SonyAudioInput input) {
inputCache[zone].putValue(input);
switch (zone) {
case 0:
updateState(SonyAudioBindingConstants.CHANNEL_INPUT, inputSource(input.input));
break;
case 1:
updateState(SonyAudioBindingConstants.CHANNEL_ZONE1_INPUT, inputSource(input.input));
break;
case 2:
updateState(SonyAudioBindingConstants.CHANNEL_ZONE2_INPUT, inputSource(input.input));
break;
case 3:
updateState(SonyAudioBindingConstants.CHANNEL_ZONE3_INPUT, inputSource(input.input));
break;
case 4:
updateState(SonyAudioBindingConstants.CHANNEL_ZONE4_INPUT, inputSource(input.input));
break;
}
if (input.radio_freq.isPresent()) {
updateState(SonyAudioBindingConstants.CHANNEL_RADIO_FREQ,
new DecimalType(input.radio_freq.get() / 1000000.0));
}
}
public abstract StringType inputSource(String input);
@Override
public void updateCurrentRadioStation(int radioStation) {
currentRadioStation = radioStation;
updateState(SonyAudioBindingConstants.CHANNEL_RADIO_STATION, new DecimalType(currentRadioStation));
}
@Override
public void updateSeekStation(String seek) {
updateState(SonyAudioBindingConstants.CHANNEL_RADIO_SEEK_STATION, new StringType(seek));
}
@Override
public void updateVolume(int zone, SonyAudioConnection.SonyAudioVolume volume) {
volumeCache[zone].putValue(volume);
switch (zone) {
case 0:
updateState(SonyAudioBindingConstants.CHANNEL_VOLUME, new PercentType(volume.volume));
updateState(SonyAudioBindingConstants.CHANNEL_MUTE, volume.mute ? OnOffType.ON : OnOffType.OFF);
break;
case 1:
updateState(SonyAudioBindingConstants.CHANNEL_ZONE1_VOLUME, new PercentType(volume.volume));
updateState(SonyAudioBindingConstants.CHANNEL_ZONE1_MUTE, volume.mute ? OnOffType.ON : OnOffType.OFF);
break;
case 2:
updateState(SonyAudioBindingConstants.CHANNEL_ZONE2_VOLUME, new PercentType(volume.volume));
updateState(SonyAudioBindingConstants.CHANNEL_ZONE2_MUTE, volume.mute ? OnOffType.ON : OnOffType.OFF);
break;
case 3:
updateState(SonyAudioBindingConstants.CHANNEL_ZONE3_VOLUME, new PercentType(volume.volume));
updateState(SonyAudioBindingConstants.CHANNEL_ZONE3_MUTE, volume.mute ? OnOffType.ON : OnOffType.OFF);
break;
case 4:
updateState(SonyAudioBindingConstants.CHANNEL_ZONE4_VOLUME, new PercentType(volume.volume));
updateState(SonyAudioBindingConstants.CHANNEL_ZONE4_MUTE, volume.mute ? OnOffType.ON : OnOffType.OFF);
break;
}
}
@Override
public void updatePowerStatus(int zone, boolean power) {
powerCache[zone].invalidateValue();
switch (zone) {
case 0:
updateState(SonyAudioBindingConstants.CHANNEL_POWER, power ? OnOffType.ON : OnOffType.OFF);
updateState(SonyAudioBindingConstants.CHANNEL_MASTER_POWER, power ? OnOffType.ON : OnOffType.OFF);
break;
case 1:
updateState(SonyAudioBindingConstants.CHANNEL_ZONE1_POWER, power ? OnOffType.ON : OnOffType.OFF);
break;
case 2:
updateState(SonyAudioBindingConstants.CHANNEL_ZONE2_POWER, power ? OnOffType.ON : OnOffType.OFF);
break;
case 3:
updateState(SonyAudioBindingConstants.CHANNEL_ZONE3_POWER, power ? OnOffType.ON : OnOffType.OFF);
break;
case 4:
updateState(SonyAudioBindingConstants.CHANNEL_ZONE4_POWER, power ? OnOffType.ON : OnOffType.OFF);
break;
}
}
private void startAutomaticRefresh(int refresh) {
if (refresh <= 0) {
return;
}
refreshJob = scheduler.scheduleWithFixedDelay(() -> {
List<Channel> channels = getThing().getChannels();
for (Channel channel : channels) {
if (!isLinked(channel.getUID())) {
continue;
}
handleCommand(channel.getUID(), RefreshType.REFRESH);
}
}, 5, refresh, TimeUnit.SECONDS);
}
}

View File

@@ -0,0 +1,112 @@
/**
* 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.sonyaudio.internal.handler;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.CompletionException;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SrsZr5Handler} is responsible for handling commands for SRS-ZR5, which are
* sent to one of the channels.
*
* @author David Åberg - Initial contribution
*/
public class SrsZr5Handler extends SonyAudioHandler {
private final Logger logger = LoggerFactory.getLogger(SrsZr5Handler.class);
public SrsZr5Handler(Thing thing, WebSocketClient webSocketClient) {
super(thing, webSocketClient);
}
@Override
public String setInputCommand(Command command) {
switch (command.toString().toLowerCase()) {
case "btaudio":
return "extInput:btAudio";
case "usb":
return "storage:usb1";
case "analog":
return "extInput:line?port=1";
case "hdmi":
return "extInput:hdmi";
case "network":
return "dlna:music";
case "cast":
return "cast:audio";
}
return command.toString();
}
@Override
public StringType inputSource(String input) {
String in = input.toLowerCase();
if (in.contains("extinput:btaudio".toLowerCase())) {
return new StringType("btaudio");
}
if (in.contains("storage:usb1".toLowerCase())) {
return new StringType("usb");
}
if (in.contains("extinput:line?port=1".toLowerCase())) {
return new StringType("analog");
}
if (in.contains("extinput:hdmi".toLowerCase())) {
return new StringType("hdmi1");
}
if (in.contains("dlna:music".toLowerCase())) {
return new StringType("network");
}
if (in.contains("cast:audio".toLowerCase())) {
return new StringType("cast");
}
return new StringType(input);
}
@Override
public void handleSoundSettings(Command command, ChannelUID channelUID) throws IOException {
if (command instanceof RefreshType) {
try {
logger.debug("SrsZr5Handler handleSoundSettings RefreshType");
Map<String, String> result = soundSettingsCache.getValue();
if (result != null) {
logger.debug("SrsZr5Handler Updating sound field to {} {}", result.get("clearAudio"),
result.get("soundField"));
if (result.get("clearAudio").equalsIgnoreCase("on")) {
updateState(channelUID, new StringType("clearAudio"));
} else {
updateState(channelUID, new StringType(result.get("soundField")));
}
}
} catch (CompletionException ex) {
throw new IOException(ex.getCause());
}
}
if (command instanceof StringType) {
if (((StringType) command).toString().equalsIgnoreCase("clearAudio")) {
connection.setSoundSettings("clearAudio", "on");
} else {
connection.setSoundSettings("soundField", ((StringType) command).toString());
}
}
}
}

View File

@@ -0,0 +1,148 @@
/**
* 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.sonyaudio.internal.handler;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.CompletionException;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link StrDn1080Handler} is responsible for handling commands for STR-DN1080, which are
* sent to one of the channels.
*
* @author David Åberg - Initial contribution
*/
public class StrDn1080Handler extends SonyAudioHandler {
private final Logger logger = LoggerFactory.getLogger(StrDn1080Handler.class);
public StrDn1080Handler(Thing thing, WebSocketClient webSocketClient) {
super(thing, webSocketClient);
}
@Override
public String setInputCommand(Command command) {
switch (command.toString().toLowerCase()) {
case "btaudio":
return "extInput:btAudio";
case "fm":
return "radio:fm";
case "usb":
return "storage:usb1";
case "bd/dvd":
return "extInput:bd-dvd";
case "game":
return "extInput:game";
case "sat/catv":
return "extInput:sat-catv";
case "video1":
return "extInput:video?port=1";
case "video2":
return "extInput:video?port=2";
case "tv":
return "extInput:tv";
case "sa-cd/cd":
return "extInput:sacd-cd";
case "network":
return "dlna:music";
case "source":
return "extInput:source";
case "cast":
return "cast:audio";
}
return command.toString();
}
@Override
public StringType inputSource(String input) {
String in = input.toLowerCase();
if (in.contains("extinput:btaudio".toLowerCase())) {
return new StringType("btaudio");
}
if (in.contains("radio:fm".toLowerCase())) {
return new StringType("fm");
}
if (in.contains("storage:usb1".toLowerCase())) {
return new StringType("usb");
}
if (in.contains("extInput:bd-dvd".toLowerCase())) {
return new StringType("bd/dvd");
}
if (in.contains("extInput:game".toLowerCase())) {
return new StringType("game");
}
if (in.contains("extInput:sat-catv".toLowerCase())) {
return new StringType("sat/catv");
}
if (in.contains("extInput:video?port=1".toLowerCase())) {
return new StringType("video1");
}
if (in.contains("extInput:video?port=2".toLowerCase())) {
return new StringType("video2");
}
if (in.contains("extinput:tv".toLowerCase())) {
return new StringType("tv");
}
if (in.contains("extInput:sacd-cd".toLowerCase())) {
return new StringType("sa-cd/cd");
}
if (in.contains("dlna:music".toLowerCase())) {
return new StringType("network");
}
if (in.contains("extInput:source".toLowerCase())) {
return new StringType("source");
}
if (in.contains("cast:audio".toLowerCase())) {
return new StringType("cast");
}
return new StringType(input);
}
@Override
public void handleSoundSettings(Command command, ChannelUID channelUID) throws IOException {
if (command instanceof RefreshType) {
try {
logger.debug("StrDn1080Handler handleSoundSettings RefreshType");
Map<String, String> result = soundSettingsCache.getValue();
if (result != null) {
logger.debug("StrDn1080Handler Updateing sound field to {} {}", result.get("pureDirect"),
result.get("soundField"));
if (result.get("pureDirect").equalsIgnoreCase("on")) {
updateState(channelUID, new StringType("pureDirect"));
} else {
updateState(channelUID, new StringType(result.get("soundField")));
}
}
} catch (CompletionException ex) {
throw new IOException(ex.getCause());
}
}
if (command instanceof StringType) {
if (((StringType) command).toString().equalsIgnoreCase("pureDirect")) {
connection.setSoundSettings("pureDirect", "on");
} else {
connection.setSoundSettings("soundField", ((StringType) command).toString());
}
}
}
}

View File

@@ -0,0 +1,246 @@
/**
* 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.sonyaudio.internal.protocol;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.websocket.api.RemoteEndpoint;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
/**
* The {@link SonyAudioConnection} is responsible for communicating with SONY audio products
* handlers.
*
* @author David Åberg - Initial contribution
*/
public class SonyAudioClientSocket {
private final Logger logger = LoggerFactory.getLogger(SonyAudioClientSocket.class);
private final ScheduledExecutorService scheduler;
private static final int REQUEST_TIMEOUT_MS = 60000;
private CountDownLatch commandLatch = null;
private JsonObject commandResponse = null;
private int nextMessageId = 1;
private boolean connected = false;
private final URI uri;
private Session session;
private final JsonParser parser = new JsonParser();
private final Gson mapper;
private static int ping = 0;
private final SonyAudioClientSocketEventListener eventHandler;
public SonyAudioClientSocket(SonyAudioClientSocketEventListener eventHandler, URI uri,
ScheduledExecutorService scheduler) {
mapper = new GsonBuilder().disableHtmlEscaping().create();
this.eventHandler = eventHandler;
this.uri = uri;
this.scheduler = scheduler;
}
public synchronized void open(WebSocketClient webSocketClient) {
try {
if (isConnected()) {
logger.warn("connect: connection is already open");
}
SonyAudioWebSocketListener socket = new SonyAudioWebSocketListener();
ClientUpgradeRequest request = new ClientUpgradeRequest();
try {
webSocketClient.connect(socket, uri, request).get(1, TimeUnit.SECONDS);
} catch (TimeoutException e) {
logger.debug("Could not establish websocket within a second.");
}
} catch (Exception e) {
logger.debug("Exception then trying to start the websocket {}", e.getMessage(), e);
}
}
public void close() {
logger.debug("Closing socket {}", uri);
// if there is an old web socket then clean up and destroy
if (session != null) {
try {
session.close();
} catch (Exception e) {
logger.debug("Exception during closing the websocket session {}", e.getMessage(), e);
}
session = null;
}
}
public boolean isConnected() {
if (session == null || !session.isOpen()) {
connected = false;
return connected;
}
RemoteEndpoint remote = session.getRemote();
ByteBuffer payload = ByteBuffer.allocate(4).putInt(ping++);
try {
remote.sendPing(payload);
} catch (IOException e) {
logger.warn("Connection to {} lost: {}", uri, e.getMessage());
connected = false;
}
return connected;
}
@WebSocket
public class SonyAudioWebSocketListener {
@OnWebSocketConnect
public void onConnect(Session wssession) {
logger.debug("Connected to server");
session = wssession;
connected = true;
if (eventHandler != null) {
scheduler.submit(() -> {
try {
eventHandler.onConnectionOpened(uri);
} catch (Exception e) {
logger.error("Error handling onConnectionOpened() {}", e.getMessage(), e);
}
});
}
}
@OnWebSocketMessage
public void onMessage(String message) {
logger.debug("Message received from server: {}", message);
try {
final JsonObject json = parser.parse(message).getAsJsonObject();
if (json.has("id")) {
logger.debug("Response received from server: {}", json);
int messageId = json.get("id").getAsInt();
if (messageId == nextMessageId - 1) {
commandResponse = json;
commandLatch.countDown();
}
} else {
logger.debug("Event received from server: {}", json);
try {
if (eventHandler != null) {
scheduler.submit(() -> {
try {
eventHandler.handleEvent(json);
} catch (Exception e) {
logger.error("Error handling event {} player state change message: {}", json,
e.getMessage(), e);
}
});
}
} catch (Exception e) {
logger.error("Error handling player state change message", e);
}
}
} catch (JsonParseException e) {
logger.debug("Not valid JSON message: {}", e.getMessage(), e);
}
}
@OnWebSocketClose
public void onClose(int statusCode, String reason) {
session = null;
connected = false;
logger.debug("Closing a WebSocket due to {}", reason);
scheduler.submit(() -> {
try {
eventHandler.onConnectionClosed();
} catch (Exception e) {
logger.error("Error handling onConnectionClosed()", e);
}
});
}
@OnWebSocketError
public void onError(Throwable error) {
onClose(0, error.getMessage());
}
}
private void sendMessage(String str) throws IOException {
if (isConnected()) {
logger.debug("send message fo {}: {}", uri.toString(), str);
session.getRemote().sendString(str);
} else {
String stack = "";
stack += "Printing stack trace:\n";
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
for (int i = 1; i < elements.length; i++) {
StackTraceElement s = elements[i];
stack += "\tat " + s.getClassName() + "." + s.getMethodName() + "(" + s.getFileName() + ":"
+ s.getLineNumber() + ")\n";
}
logger.error("Socket not initialized, trying to send {} {}", str, stack);
throw new IOException("Socket not initialized");
}
}
public synchronized JsonElement callMethod(SonyAudioMethod method) throws IOException {
try {
method.id = nextMessageId;
String message = mapper.toJson(method);
logger.debug("callMethod send {}", message);
commandLatch = new CountDownLatch(1);
commandResponse = null;
nextMessageId++;
sendMessage(message);
if (commandLatch.await(REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
logger.debug("callMethod {} returns {}", uri.toString(), commandResponse.toString());
return commandResponse.get("result");
} else {
logger.debug("Timeout during callMethod({}, {})", method.method, message);
throw new IOException("Timeout during callMethod");
}
} catch (InterruptedException e) {
throw new IOException("Timeout in callMethod");
}
}
public URI getURI() {
return uri;
}
}

View File

@@ -0,0 +1,31 @@
/**
* 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.sonyaudio.internal.protocol;
import java.net.URI;
import com.google.gson.JsonObject;
/**
* The {@link SonyAudioClientSocketEventListener} socket event listener
* handlers.
*
* @author David Åberg - Initial contribution
*/
public interface SonyAudioClientSocketEventListener {
void handleEvent(JsonObject json);
void onConnectionClosed();
void onConnectionOpened(URI resource);
}

View File

@@ -0,0 +1,520 @@
/**
* 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.sonyaudio.internal.protocol;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.openhab.binding.sonyaudio.internal.SonyAudioEventListener;
import org.openhab.binding.sonyaudio.internal.protocol.SwitchNotifications.Notification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
/**
* The {@link SonyAudioConnection} is responsible for communicating with SONY audio products
* handlers.
*
* @author David Åberg - Initial contribution
*/
public class SonyAudioConnection implements SonyAudioClientSocketEventListener {
private final Logger logger = LoggerFactory.getLogger(SonyAudioConnection.class);
private final String host;
private final int port;
private final String path;
private final URI base_uri;
private final WebSocketClient webSocketClient;
private SonyAudioClientSocket avContentSocket;
private SonyAudioClientSocket audioSocket;
private SonyAudioClientSocket systemSocket;
private final SonyAudioEventListener listener;
private int min_volume = 0;
private int max_volume = 50;
private final Gson gson;
public SonyAudioConnection(String host, int port, String path, SonyAudioEventListener listener,
ScheduledExecutorService scheduler, WebSocketClient webSocketClient) throws URISyntaxException {
this.host = host;
this.port = port;
this.path = path;
this.listener = listener;
this.gson = new Gson();
this.webSocketClient = webSocketClient;
base_uri = new URI(String.format("ws://%s:%d/%s", host, port, path)).normalize();
URI wsAvContentUri = base_uri.resolve(base_uri.getPath() + "/avContent").normalize();
avContentSocket = new SonyAudioClientSocket(this, wsAvContentUri, scheduler);
URI wsAudioUri = base_uri.resolve(base_uri.getPath() + "/audio").normalize();
audioSocket = new SonyAudioClientSocket(this, wsAudioUri, scheduler);
URI wsSystemUri = base_uri.resolve(base_uri.getPath() + "/system").normalize();
systemSocket = new SonyAudioClientSocket(this, wsSystemUri, scheduler);
}
@Override
public void handleEvent(JsonObject json) {
int zone = 0;
JsonObject param;
try {
param = json.getAsJsonArray("params").get(0).getAsJsonObject();
} catch (NullPointerException e) {
logger.debug("Invalid json in handleEvent");
return;
} catch (IndexOutOfBoundsException e) {
logger.debug("Invalid json in handleEvent");
return;
}
if (param == null) {
logger.debug("Unable to get params form json in handleEvent");
return;
}
if (param.has("output")) {
String outputStr = param.get("output").getAsString();
Pattern pattern = Pattern.compile(".*zone=(\\d+)");
Matcher m = pattern.matcher(outputStr);
if (m.matches()) {
try {
zone = Integer.parseInt(m.group(1));
} catch (NumberFormatException e) {
logger.error("This should never happen, pattern should only match integers");
return;
}
}
}
if (json.get("method").getAsString().equalsIgnoreCase("notifyPlayingContentInfo")) {
SonyAudioInput input = new SonyAudioInput();
input.input = param.get("uri").getAsString();
if (param.has("broadcastFreq")) {
int freq = param.get("broadcastFreq").getAsInt();
input.radio_freq = Optional.of(freq);
checkRadioPreset(input.input);
}
listener.updateInput(zone, input);
listener.updateSeekStation("");
}
if (json.get("method").getAsString().equalsIgnoreCase("notifyVolumeInformation")) {
SonyAudioVolume volume = new SonyAudioVolume();
int rawVolume = param.get("volume").getAsInt();
volume.volume = Math.round(100 * (rawVolume - min_volume) / (max_volume - min_volume));
volume.mute = param.get("mute").getAsString().equalsIgnoreCase("on");
listener.updateVolume(zone, volume);
}
if (json.get("method").getAsString().equalsIgnoreCase("notifyPowerStatus")) {
String power = param.get("status").getAsString();
listener.updatePowerStatus(zone, power.equalsIgnoreCase("active"));
}
listener.updateConnectionState(true);
}
private void checkRadioPreset(String input) {
Pattern pattern = Pattern.compile(".*contentId=(\\d+)");
Matcher m = pattern.matcher(input);
if (m.matches()) {
listener.updateCurrentRadioStation(Integer.parseInt(m.group(1)));
}
}
@Override
public synchronized void onConnectionClosed() {
listener.updateConnectionState(false);
}
private class Notifications {
public List<Notification> enabled;
public List<Notification> disabled;
}
private Notifications getSwitches(SonyAudioClientSocket socket, Notifications notifications) throws IOException {
SwitchNotifications switchNotifications = new SwitchNotifications(notifications.enabled,
notifications.disabled);
JsonElement switches = socket.callMethod(switchNotifications);
Type notificationListType = new TypeToken<List<Notification>>() {
}.getType();
notifications.enabled = gson.fromJson(switches.getAsJsonArray().get(0).getAsJsonObject().get("enabled"),
notificationListType);
notifications.disabled = gson.fromJson(switches.getAsJsonArray().get(0).getAsJsonObject().get("disabled"),
notificationListType);
return notifications;
}
@Override
public synchronized void onConnectionOpened(URI resource) {
try {
Notifications notifications = new Notifications();
notifications.enabled = Arrays.asList(new Notification[] {});
notifications.disabled = Arrays.asList(new Notification[] {});
if (avContentSocket.getURI().equals(resource)) {
notifications = getSwitches(avContentSocket, notifications);
for (Iterator<Notification> iter = notifications.disabled.listIterator(); iter.hasNext();) {
Notification a = iter.next();
if (a.name.equalsIgnoreCase("notifyPlayingContentInfo")) {
notifications.enabled.add(a);
iter.remove();
}
}
SwitchNotifications switchNotifications = new SwitchNotifications(notifications.enabled,
notifications.disabled);
avContentSocket.callMethod(switchNotifications);
}
if (audioSocket.getURI().equals(resource)) {
notifications = getSwitches(audioSocket, notifications);
for (Iterator<Notification> iter = notifications.disabled.listIterator(); iter.hasNext();) {
Notification a = iter.next();
if (a.name.equalsIgnoreCase("notifyVolumeInformation")) {
notifications.enabled.add(a);
iter.remove();
}
}
SwitchNotifications switchNotifications = new SwitchNotifications(notifications.enabled,
notifications.disabled);
audioSocket.callMethod(switchNotifications);
}
if (systemSocket.getURI().equals(resource)) {
notifications = getSwitches(systemSocket, notifications);
for (Iterator<Notification> iter = notifications.disabled.listIterator(); iter.hasNext();) {
Notification a = iter.next();
if (a.name.equalsIgnoreCase("notifyPowerStatus")) {
notifications.enabled.add(a);
iter.remove();
}
}
SwitchNotifications switchNotifications = new SwitchNotifications(notifications.enabled,
notifications.disabled);
systemSocket.callMethod(switchNotifications);
}
listener.updateConnectionState(true);
} catch (IOException e) {
logger.debug("Failed to setup connection");
listener.updateConnectionState(false);
}
}
public synchronized void close() {
logger.debug("SonyAudio closing connections");
if (avContentSocket != null) {
avContentSocket.close();
}
avContentSocket = null;
if (audioSocket != null) {
audioSocket.close();
}
audioSocket = null;
if (systemSocket != null) {
systemSocket.close();
}
systemSocket = null;
}
private boolean checkConnection(SonyAudioClientSocket socket) {
if (!socket.isConnected()) {
logger.debug("checkConnection: try to connect to {}", socket.getURI().toString());
socket.open(webSocketClient);
return socket.isConnected();
}
return true;
}
public boolean checkConnection() {
return checkConnection(avContentSocket) && checkConnection(audioSocket) && checkConnection(systemSocket);
}
public String getConnectionName() {
if (base_uri != null) {
return base_uri.toString();
}
return String.format("ws://%s:%d/%s", host, port, path);
}
public Boolean getPower(int zone) throws IOException {
if (zone > 0) {
if (avContentSocket == null) {
throw new IOException("AvContent Socket not connected");
}
GetCurrentExternalTerminalsStatus getCurrentExternalTerminalsStatus = new GetCurrentExternalTerminalsStatus();
JsonElement element = avContentSocket.callMethod(getCurrentExternalTerminalsStatus);
if (element != null && element.isJsonArray()) {
Iterator<JsonElement> terminals = element.getAsJsonArray().get(0).getAsJsonArray().iterator();
while (terminals.hasNext()) {
JsonObject terminal = terminals.next().getAsJsonObject();
String uri = terminal.get("uri").getAsString();
if (uri.equalsIgnoreCase("extOutput:zone?zone=" + Integer.toString(zone))) {
return terminal.get("active").getAsString().equalsIgnoreCase("active") ? true : false;
}
}
}
throw new IOException(
"Unexpected responses: Unable to parse GetCurrentExternalTerminalsStatus response message");
} else {
if (systemSocket == null) {
throw new IOException("System Socket not connected");
}
GetPowerStatus getPowerStatus = new GetPowerStatus();
JsonElement element = systemSocket.callMethod(getPowerStatus);
if (element != null && element.isJsonArray()) {
String powerStatus = element.getAsJsonArray().get(0).getAsJsonObject().get("status").getAsString();
return powerStatus.equalsIgnoreCase("active") ? true : false;
}
throw new IOException("Unexpected responses: Unable to parse GetPowerStatus response message");
}
}
public void setPower(boolean power) throws IOException {
setPower(power, 0);
}
public void setPower(boolean power, int zone) throws IOException {
if (zone > 0) {
if (avContentSocket == null) {
throw new IOException("AvContent Socket not connected");
}
SetActiveTerminal setActiveTerminal = new SetActiveTerminal(power, zone);
avContentSocket.callMethod(setActiveTerminal);
} else {
if (systemSocket == null) {
throw new IOException("System Socket not connected");
}
SetPowerStatus setPowerStatus = new SetPowerStatus(power);
systemSocket.callMethod(setPowerStatus);
}
}
public class SonyAudioInput {
public String input = "";
public Optional<Integer> radio_freq = Optional.empty();
}
public SonyAudioInput getInput() throws IOException {
GetPlayingContentInfo getPlayingContentInfo = new GetPlayingContentInfo();
return getInput(getPlayingContentInfo);
}
public SonyAudioInput getInput(int zone) throws IOException {
GetPlayingContentInfo getPlayingContentInfo = new GetPlayingContentInfo(zone);
return getInput(getPlayingContentInfo);
}
private SonyAudioInput getInput(GetPlayingContentInfo getPlayingContentInfo) throws IOException {
if (avContentSocket == null) {
throw new IOException("AvContent Socket not connected");
}
JsonElement element = avContentSocket.callMethod(getPlayingContentInfo);
if (element != null && element.isJsonArray()) {
SonyAudioInput ret = new SonyAudioInput();
JsonObject result = element.getAsJsonArray().get(0).getAsJsonArray().get(0).getAsJsonObject();
String uri = result.get("uri").getAsString();
checkRadioPreset(uri);
ret.input = uri;
if (result.has("broadcastFreq")) {
int freq = result.get("broadcastFreq").getAsInt();
ret.radio_freq = Optional.of(freq);
}
return ret;
}
throw new IOException("Unexpected responses: Unable to parse GetPlayingContentInfo response message");
}
public void setInput(String input) throws IOException {
if (avContentSocket == null) {
throw new IOException("AvContent Socket not connected");
}
SetPlayContent setPlayContent = new SetPlayContent(input);
avContentSocket.callMethod(setPlayContent);
}
public void setInput(String input, int zone) throws IOException {
if (avContentSocket == null) {
throw new IOException("AvContent Socket not connected");
}
SetPlayContent setPlayContent = new SetPlayContent(input, zone);
avContentSocket.callMethod(setPlayContent);
}
public void radioSeekFwd() throws IOException {
if (avContentSocket == null) {
throw new IOException("AvContent Socket not connected");
}
SeekBroadcastStation seekBroadcastStation = new SeekBroadcastStation(true);
avContentSocket.callMethod(seekBroadcastStation);
}
public void radioSeekBwd() throws IOException {
if (avContentSocket == null) {
throw new IOException("AvContent Socket not connected");
}
SeekBroadcastStation seekBroadcastStation = new SeekBroadcastStation(false);
avContentSocket.callMethod(seekBroadcastStation);
}
public class SonyAudioVolume {
public Integer volume = 0;
public Boolean mute = false;
}
public SonyAudioVolume getVolume(int zone) throws IOException {
GetVolumeInformation getVolumeInformation = new GetVolumeInformation(zone);
if (audioSocket == null || !audioSocket.isConnected()) {
throw new IOException("Audio Socket not connected");
}
JsonElement element = audioSocket.callMethod(getVolumeInformation);
if (element != null && element.isJsonArray()) {
JsonObject result = element.getAsJsonArray().get(0).getAsJsonArray().get(0).getAsJsonObject();
SonyAudioVolume ret = new SonyAudioVolume();
int volume = result.get("volume").getAsInt();
min_volume = result.get("minVolume").getAsInt();
max_volume = result.get("maxVolume").getAsInt();
int vol = Math.round(100 * (volume - min_volume) / (max_volume - min_volume));
if (vol < 0) {
vol = 0;
}
ret.volume = vol;
String mute = result.get("mute").getAsString();
ret.mute = mute.equalsIgnoreCase("on") ? true : false;
return ret;
}
throw new IOException("Unexpected responses: Unable to parse GetVolumeInformation response message");
}
public void setVolume(int volume) throws IOException {
if (audioSocket == null) {
throw new IOException("Audio Socket not connected");
}
SetAudioVolume setAudioVolume = new SetAudioVolume(volume, min_volume, max_volume);
audioSocket.callMethod(setAudioVolume);
}
public void setVolume(String volumeChange) throws IOException {
if (audioSocket == null) {
throw new IOException("Audio Socket not connected");
}
SetAudioVolume setAudioVolume = new SetAudioVolume(volumeChange);
audioSocket.callMethod(setAudioVolume);
}
public void setVolume(int volume, int zone) throws IOException {
if (audioSocket == null) {
throw new IOException("Audio Socket not connected");
}
SetAudioVolume setAudioVolume = new SetAudioVolume(zone, volume, min_volume, max_volume);
audioSocket.callMethod(setAudioVolume);
}
public void setVolume(String volumeChange, int zone) throws IOException {
if (audioSocket == null) {
throw new IOException("Audio Socket not connected");
}
SetAudioVolume setAudioVolume = new SetAudioVolume(zone, volumeChange);
audioSocket.callMethod(setAudioVolume);
}
public void setMute(boolean mute) throws IOException {
if (audioSocket == null) {
throw new IOException("Audio Socket not connected");
}
SetAudioMute setAudioMute = new SetAudioMute(mute);
audioSocket.callMethod(setAudioMute);
}
public void setMute(boolean mute, int zone) throws IOException {
if (audioSocket == null) {
throw new IOException("Audio Socket not connected");
}
SetAudioMute setAudioMute = new SetAudioMute(mute, zone);
audioSocket.callMethod(setAudioMute);
}
public Map<String, String> getSoundSettings() throws IOException {
if (audioSocket == null) {
throw new IOException("Audio Socket not connected");
}
Map<String, String> m = new HashMap<>();
GetSoundSettings getSoundSettings = new GetSoundSettings();
JsonElement element = audioSocket.callMethod(getSoundSettings);
if (element == null || !element.isJsonArray()) {
throw new IOException("Unexpected responses: Unable to parse GetSoundSettings response message");
}
Iterator<JsonElement> iterator = element.getAsJsonArray().get(0).getAsJsonArray().iterator();
while (iterator.hasNext()) {
JsonObject item = iterator.next().getAsJsonObject();
m.put(item.get("target").getAsString(), item.get("currentValue").getAsString());
}
return m;
}
public void setSoundSettings(String target, String value) throws IOException {
if (audioSocket == null) {
throw new IOException("Audio Socket not connected");
}
SetSoundSettings setSoundSettings = new SetSoundSettings(target, value);
audioSocket.callMethod(setSoundSettings);
}
}

View File

@@ -0,0 +1,459 @@
/**
* 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.sonyaudio.internal.protocol;
import java.util.List;
/**
* The {@link SonyAudioMethod} base class for SONY API methods
*
* @author David Åberg - Initial contribution
*/
public abstract class SonyAudioMethod {
protected int id = 1;
protected String method;
protected String version;
public SonyAudioMethod(String method, String version) {
this.method = method;
this.version = version;
}
}
/**
* The {@link GetPowerStatus} SONY Audio control API method
*
* @author David Åberg - Initial contribution
*/
class GetPowerStatus extends SonyAudioMethod {
public String[] params = new String[] {};
public GetPowerStatus() {
super("getPowerStatus", "1.1");
}
}
/**
* The {@link GetCurrentExternalTerminalsStatus} SONY Audio control API method
*
* @author David Åberg - Initial contribution
*/
class GetCurrentExternalTerminalsStatus extends SonyAudioMethod {
public String[] params = new String[] {};
public GetCurrentExternalTerminalsStatus() {
super("getCurrentExternalTerminalsStatus", "1.0");
}
}
/**
* The {@link SetPowerStatus} SONY Audio control API method
*
* @author David Åberg - Initial contribution
*/
class SetPowerStatus extends SonyAudioMethod {
public Param[] params;
class Param {
public String status;
Param(boolean power) {
status = power ? "active" : "off";
}
}
SetPowerStatus(boolean power) {
super("setPowerStatus", "1.1");
Param param = new Param(power);
params = new Param[] { param };
}
}
/**
* The {@link SetActiveTerminal} SONY Audio control API method
*
* @author David Åberg - Initial contribution
*/
class SetActiveTerminal extends SonyAudioMethod {
public Param[] params;
class Param {
public String active;
public String uri;
Param(boolean power, int zone) {
active = power ? "active" : "inactive";
if (zone > 0) {
uri = "extOutput:zone?zone=" + Integer.toString(zone);
}
}
}
SetActiveTerminal(boolean power, int zone) {
super("setActiveTerminal", "1.0");
Param param = new Param(power, zone);
params = new Param[] { param };
}
}
/**
* The {@link GetPlayingContentInfo} SONY Audio control API method
*
* @author David Åberg - Initial contribution
*/
class GetPlayingContentInfo extends SonyAudioMethod {
public Param[] params;
class Param {
String output = "";
Param() {
}
Param(int zone) {
if (zone > 0) {
output = "extOutput:zone?zone=" + Integer.toString(zone);
}
}
}
GetPlayingContentInfo() {
super("getPlayingContentInfo", "1.2");
Param param = new Param();
params = new Param[] { param };
}
GetPlayingContentInfo(int zone) {
super("getPlayingContentInfo", "1.2");
Param param = new Param(zone);
params = new Param[] { param };
}
}
/**
* The {@link SetPlayContent} SONY Audio control API method
*
* @author David Åberg - Initial contribution
*/
class SetPlayContent extends SonyAudioMethod {
public Param[] params;
class Param {
String output = "";
String uri;
Param(String input) {
uri = input;
}
Param(String input, int zone) {
uri = input;
if (zone > 0) {
output = "extOutput:zone?zone=" + Integer.toString(zone);
}
}
}
SetPlayContent(String input) {
super("setPlayContent", "1.2");
params = new Param[] { new Param(input) };
}
SetPlayContent(String input, int zone) {
super("setPlayContent", "1.2");
params = new Param[] { new Param(input, zone) };
}
}
/**
* The {@link GetVolumeInformation} SONY Audio control API method
*
* @author David Åberg - Initial contribution
*/
class GetVolumeInformation extends SonyAudioMethod {
public Param[] params;
class Param {
String output = "";
Param() {
}
Param(int zone) {
if (zone > 0) {
output = "extOutput:zone?zone=" + Integer.toString(zone);
}
}
}
GetVolumeInformation() {
this(0);
}
GetVolumeInformation(int zone) {
super("getVolumeInformation", "1.1");
params = new Param[] { new Param(zone) };
}
}
/**
* The {@link SetAudioVolume} SONY Audio control API method
*
* @author David Åberg - Initial contribution
*/
class SetAudioVolume extends SonyAudioMethod {
public Param[] params;
class Param {
String output = "";
String volume;
Param(long new_volume) {
volume = Long.toString(new_volume);
}
Param(String volume_change) {
volume = volume_change;
}
Param(long new_volume, int zone) {
volume = Long.toString(new_volume);
if (zone > 0) {
output = "extOutput:zone?zone=" + Integer.toString(zone);
}
}
Param(String volume_change, int zone) {
volume = volume_change;
if (zone > 0) {
output = "extOutput:zone?zone=" + Integer.toString(zone);
}
}
}
SetAudioVolume(int volume, int min, int max) {
super("setAudioVolume", "1.1");
long scaled_volume = scaleVolume(volume, min, max);
params = new Param[] { new Param(scaled_volume) };
}
SetAudioVolume(int zone, int volume, int min, int max) {
super("setAudioVolume", "1.1");
long scaled_volume = scaleVolume(volume, min, max);
params = new Param[] { new Param(scaled_volume, zone) };
}
SetAudioVolume(String volume_change) {
super("setAudioVolume", "1.1");
params = new Param[] { new Param(volume_change) };
}
SetAudioVolume(int zone, String volume_change) {
super("setAudioVolume", "1.1");
params = new Param[] { new Param(volume_change, zone) };
}
long scaleVolume(int volume, int min, int max) {
return Math.round(((max - min) * volume / 100.0) + min);
}
}
/**
* The {@link SetAudioMute} SONY Audio control API method
*
* @author David Åberg - Initial contribution
*/
class SetAudioMute extends SonyAudioMethod {
public Param[] params;
class Param {
String output = "";
String mute;
Param(boolean mute) {
this.mute = mute ? "on" : "off";
}
Param(boolean mute, int zone) {
this.mute = mute ? "on" : "off";
if (zone > 0) {
output = "extOutput:zone?zone=" + Integer.toString(zone);
}
}
}
SetAudioMute(boolean mute) {
super("setAudioMute", "1.1");
params = new Param[] { new Param(mute) };
}
SetAudioMute(boolean mute, int zone) {
super("setAudioMute", "1.1");
params = new Param[] { new Param(mute, zone) };
}
}
/**
* Helper class
*
* @author David Åberg - Initial contribution
*/
class GetSoundSettings extends SonyAudioMethod {
public Param[] params;
class Param {
String target;
Param(String target) {
this.target = target;
}
}
GetSoundSettings() {
super("getSoundSettings", "1.1");
params = new Param[] { new Param("") };
}
GetSoundSettings(String target) {
super("getSoundSettings", "1.1");
params = new Param[] { new Param(target) };
}
}
/**
* Helper class
*
* @author David Åberg - Initial contribution
*/
class SetSoundSettings extends SonyAudioMethod {
public Param[] params;
class Settings {
String value;
String target;
Settings(String target, String value) {
this.target = target;
this.value = value;
}
}
class Param {
Settings[] settings;
Param(Settings[] settings) {
this.settings = settings;
}
}
SetSoundSettings() {
super("setSoundSettings", "1.1");
}
SetSoundSettings(String target, String value) {
super("setSoundSettings", "1.1");
Settings[] settings = { new Settings(target, value) };
params = new Param[] { new Param(settings) };
}
}
/**
* The {@link SetSoundField} SONY Audio control API method
*
* @author David Åberg - Initial contribution
*/
class SetSoundField extends SetSoundSettings {
SetSoundField(String soundField) {
super("soundField", soundField);
}
}
/**
* The {@link SetPureDirect} SONY Audio control API method
*
* @author David Åberg - Initial contribution
*/
class SetPureDirect extends SetSoundSettings {
SetPureDirect(boolean pureDirect) {
super("pureDirect", pureDirect ? "on" : "off");
}
}
/**
* The {@link SetClearAudio} SONY Audio control API method
*
* @author David Åberg - Initial contribution
*/
class SetClearAudio extends SetSoundSettings {
SetClearAudio(boolean clearAudio) {
super("clearAudio", clearAudio ? "on" : "off");
}
}
/**
* The {@link SwitchNotifications} SONY Audio control API method
*
* @author David Åberg - Initial contribution
*/
class SwitchNotifications extends SonyAudioMethod {
public Param[] params;
class Notification {
String name;
String version;
@Override
public String toString() {
return "Notification{name='" + name + "' version='" + version + "'}";
}
}
class Param {
List<Notification> enabled;
List<Notification> disabled;
Param(List<Notification> enabled, List<Notification> disabled) {
this.enabled = enabled;
this.disabled = disabled;
}
}
SwitchNotifications(List<Notification> enabled, List<Notification> disabled) {
super("switchNotifications", "1.0");
params = new Param[] { new Param(!enabled.isEmpty() ? enabled : null, !disabled.isEmpty() ? disabled : null) };
}
}
/**
* The {@link SeekBroadcastStation} SONY Audio control API method
*
* @author David Åberg - Initial contribution
*/
class SeekBroadcastStation extends SonyAudioMethod {
public Param[] params;
class Param {
String tuning = "auto";
String direction;
Param(boolean forward) {
this.direction = forward ? "fwd" : "bwd";
}
}
SeekBroadcastStation(boolean forward) {
super("seekBroadcastStation", "1.0");
params = new Param[] { new Param(forward) };
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="sonyaudio" 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>SonyAudio Binding</name>
<description>This is the binding for SonyAudio products (Receivers and wireless speakers).</description>
<author>David Åberg</author>
</binding:binding>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="thing-type:sonyaudio:config">
<parameter name="ipAddress" type="text">
<label>Network Address</label>
<description>The IP or host name of the SONY audio device</description>
<context>network-address</context>
</parameter>
<parameter name="port" type="integer" min="1" max="65535">
<label>Port</label>
<description>Port for the SONY audio device to control.
Home Audio products usually use port 10000 and Personal Audio
products usually use port 54480.</description>
<default>10000</default>
</parameter>
<parameter name="path" type="text">
<label>Base Path</label>
<description>The base path, "/sony" in most cases</description>
<default>/sony</default>
<advanced>true</advanced>
</parameter>
<parameter name="refreshInterval" type="integer" min="0">
<label>Refresh Interval</label>
<description>The refresh interval in seconds for polling the receiver (0=disable). Binding receive automatically
updates from
</description>
<default>300</default>
<advanced>true</advanced>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="sonyaudio"
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="input-ct800">
<item-type>String</item-type>
<label>Input Source</label>
<description>Select the input source of the receiver</description>
<state>
<options>
<option value="btaudio">Bluetooth Audio</option>
<option value="tv">TV</option>
<option value="hdmi1">HDMI1</option>
<option value="hdmi2">HDMI2</option>
<option value="hdmi3">HDMI3</option>
<option value="analog">Analog</option>
<option value="usb">USB</option>
<option value="network">Home Network</option>
<option value="cast">Chromecast</option>
</options>
</state>
</channel-type>
<channel-type id="sound-field-ct800">
<item-type>String</item-type>
<label>Sound Field</label>
<description>Select the Sound Field for the receiver</description>
<state>
<options>
<option value="clearAudio">ClearAudio+</option>
<option value="movie">Movie</option>
<option value="music">Music</option>
<option value="sports">Sports</option>
<option value="game">Game Studio</option>
<option value="standard">Standard</option>
</options>
</state>
</channel-type>
<!-- HT-CT800 Thing Type -->
<thing-type id="HT-CT800">
<label>SONY Soundbar HT-CT800</label>
<description>SONY Soundbar HT-CT800</description>
<channels>
<channel id="power" typeId="power"/>
<channel id="input" typeId="input-ct800"/>
<channel id="volume" typeId="volume"/>
<channel id="mute" typeId="mute"/>
<channel id="soundField" typeId="sound-field-ct800"/>
</channels>
<config-description-ref uri="thing-type:sonyaudio:config"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="sonyaudio"
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="input-mt500">
<item-type>String</item-type>
<label>Input Source</label>
<description>Select the input source of the receiver</description>
<state>
<options>
<option value="btaudio">Bluetooth Audio</option>
<option value="tv">TV</option>
<option value="analog">Analog</option>
<option value="usb">USB</option>
<option value="network">Home Network</option>
<option value="cast">Chromecast</option>
</options>
</state>
</channel-type>
<channel-type id="sound-field-mt500">
<item-type>String</item-type>
<label>Sound Field</label>
<description>Select the Sound Field for the receiver</description>
<state>
<options>
<option value="clearAudio">ClearAudio+</option>
<option value="movie">Movie</option>
<option value="music">Music</option>
<option value="sports">Sports</option>
<option value="game">Game Studio</option>
<option value="standard">Standard</option>
</options>
</state>
</channel-type>
<!-- HT-MT500 Thing Type -->
<thing-type id="HT-MT500">
<label>SONY Soundbar HT-MT500</label>
<description>SONY Soundbar HT-MT500</description>
<channels>
<channel id="power" typeId="power"/>
<channel id="input" typeId="input-mt500"/>
<channel id="volume" typeId="volume"/>
<channel id="mute" typeId="mute"/>
<channel id="soundField" typeId="sound-field-mt500"/>
</channels>
<config-description-ref uri="thing-type:sonyaudio:config"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="sonyaudio"
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="input-st5000">
<item-type>String</item-type>
<label>Input Source</label>
<description>Select the input source of the receiver</description>
<state>
<options>
<option value="btaudio">Bluetooth Audio</option>
<option value="tv">TV</option>
<option value="hdmi1">HDMI1</option>
<option value="hdmi2">HDMI2</option>
<option value="hdmi3">HDMI3</option>
<option value="analog">Analog</option>
<option value="usb">USB</option>
<option value="network">Home Network</option>
<option value="cast">Chromecast</option>
</options>
</state>
</channel-type>
<channel-type id="sound-field-st5000">
<item-type>String</item-type>
<label>Sound Field</label>
<description>Select the Sound Field for the receiver</description>
<state>
<options>
<option value="clearAudio">ClearAudio+</option>
<option value="3dsurround">3D Surround</option>
<option value="movie">Movie</option>
<option value="music">Music</option>
<option value="sports">Sports</option>
<option value="game">Game Studio</option>
<option value="standard">Standard</option>
</options>
</state>
</channel-type>
<!-- HT-ST5000 Thing Type -->
<thing-type id="HT-ST5000">
<label>SONY Soundbar HT-ST5000</label>
<description>SONY Soundbar HT-ST5000</description>
<channels>
<channel id="power" typeId="power"/>
<channel id="input" typeId="input-st5000"/>
<channel id="volume" typeId="volume"/>
<channel id="mute" typeId="mute"/>
<channel id="soundField" typeId="sound-field-st5000"/>
</channels>
<config-description-ref uri="thing-type:sonyaudio:config"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="sonyaudio"
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="input-z9f-zf9">
<item-type>String</item-type>
<label>Input Source</label>
<description>Select the input source of the receiver</description>
<state>
<options>
<option value="btaudio">Bluetooth Audio</option>
<option value="tv">TV</option>
<option value="hdmi1">HDMI1</option>
<option value="hdmi2">HDMI2</option>
<option value="analog">Analog</option>
<option value="usb">USB</option>
<option value="network">Home Network</option>
<option value="cast">Chromecast</option>
</options>
</state>
</channel-type>
<channel-type id="sound-field-z9f-zf9">
<item-type>String</item-type>
<label>Sound Field</label>
<description>Select the Sound Field for the receiver</description>
<state>
<options>
<option value="auto">Auto Sound</option>
<option value="news">News</option>
<option value="cinemaStudio">Cinema</option>
<option value="music">Music</option>
<option value="sports">Sports</option>
<option value="game">Game Studio</option>
<option value="standard">Standard</option>
</options>
</state>
</channel-type>
<channel-type id="nightMode">
<item-type>Switch</item-type>
<label>Night Mode</label>
<description>Enable/Disable Night Mode</description>
</channel-type>
<!-- HT-z9f Thing Type -->
<thing-type id="HT-Z9F">
<label>SONY Soundbar HT-Z9F</label>
<description>SONY Soundbar HT-Z9F</description>
<channels>
<channel id="power" typeId="power"/>
<channel id="input" typeId="input-z9f-zf9"/>
<channel id="volume" typeId="volume"/>
<channel id="mute" typeId="mute"/>
<channel id="soundField" typeId="sound-field-z9f-zf9"/>
</channels>
<config-description-ref uri="thing-type:sonyaudio:config"/>
</thing-type>
<!-- HT-zf9 Thing Type -->
<thing-type id="HT-ZF9">
<label>SONY Soundbar HT-ZF9</label>
<description>SONY Soundbar HT-ZF9</description>
<channels>
<channel id="power" typeId="power"/>
<channel id="input" typeId="input-z9f-zf9"/>
<channel id="volume" typeId="volume"/>
<channel id="mute" typeId="mute"/>
<channel id="soundField" typeId="sound-field-z9f-zf9"/>
<channel id="nightMode" typeId="nightMode"/>
</channels>
<config-description-ref uri="thing-type:sonyaudio:config"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="sonyaudio"
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="input-zr5">
<item-type>String</item-type>
<label>Input Source</label>
<description>Select the input source of the speaker</description>
<state>
<options>
<option value="btaudio">Bluetooth Audio</option>
<option value="usb">USB</option>
<option value="analog">Analog</option>
<option value="hdmi1">HDMI</option>
<option value="network">Home Network</option>
<option value="cast">Chromecast</option>
</options>
</state>
</channel-type>
<channel-type id="sound-field-zr5">
<item-type>String</item-type>
<label>Sound Field</label>
<description>Select the Sound Field for the receiver</description>
<state>
<options>
<option value="clearAudio">ClearAudio+</option>
<option value="hiphop">R&amp;B / HIP HOP</option>
<option value="standard">FLAT</option>
<option value="rock">ROCK</option>
<option value="pop">POP</option>
<option value="latin">LATIN</option>
<option value="jazz">JAZZ</option>
<option value="classic">CLASSIC</option>
<option value="custom">CUSTOM</option>
</options>
</state>
</channel-type>
<!-- SRS-ZR5 Thing Type -->
<thing-type id="SRS-ZR5">
<label>SONY Wireless Speaker SRS-ZR5</label>
<description>SONY wireless Speaker SRS-ZR5</description>
<channels>
<channel id="power" typeId="power"/>
<channel id="input" typeId="input-zr5"/>
<channel id="volume" typeId="volume"/>
<channel id="mute" typeId="mute"/>
<channel id="soundField" typeId="sound-field-zr5"/>
</channels>
<config-description-ref uri="thing-type:sonyaudio:config"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,167 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="sonyaudio"
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">
<channel-group-type id="masterControls">
<label>Master</label>
<channels>
<channel id="power" typeId="power"/>
<channel id="soundField" typeId="sound-field-dn1080"/>
</channels>
</channel-group-type>
<channel-group-type id="zone1Controls">
<label>Main Zone</label>
<channels>
<channel id="power" typeId="power"/>
<channel id="input" typeId="input-dn1080-zone1"/>
<channel id="volume" typeId="volume"/>
<channel id="mute" typeId="mute"/>
</channels>
</channel-group-type>
<channel-group-type id="zone2Controls">
<label>Zone 2</label>
<channels>
<channel id="power" typeId="power"/>
<channel id="input" typeId="input-dn1080-zone2"/>
<channel id="volume" typeId="volume"/>
<channel id="mute" typeId="mute"/>
</channels>
</channel-group-type>
<channel-group-type id="zone4Controls">
<label>HDMI Zone</label>
<channels>
<channel id="power" typeId="power"/>
<channel id="input" typeId="input-dn1080-zone4"/>
</channels>
</channel-group-type>
<channel-group-type id="radioControls">
<label>Radio</label>
<channels>
<channel id="broadcastFreq" typeId="radioBroadcastFreq"/>
<channel id="broadcastStation" typeId="radioBroadcastStation"/>
<channel id="broadcastSeekStation" typeId="radioSeekBroadcastStation"/>
</channels>
</channel-group-type>
<!-- Commands -->
<channel-type id="radioBroadcastFreq">
<item-type>Number</item-type>
<label>Broadcast Frequency</label>
<description>The broadcast frequency</description>
<state pattern="%.2f MHz" readOnly="true"></state>
</channel-type>
<channel-type id="radioBroadcastStation">
<item-type>Number</item-type>
<label>Broadcast Station</label>
<description>Select preset broadcast station</description>
</channel-type>
<channel-type id="radioSeekBroadcastStation">
<item-type>String</item-type>
<label>Change Broadcast Station</label>
<description>Change broadcast station</description>
<state>
<options>
<option value="fwdSeeking">Seek Forward</option>
<option value="bwdSeeking">Seek Backward</option>
</options>
</state>
</channel-type>
<channel-type id="input-dn1080-zone1">
<item-type>String</item-type>
<label>Input Source</label>
<description>Select the input source of the receiver</description>
<state>
<options>
<option value="btaudio">Bluetooth Audio</option>
<option value="fm">FM</option>
<option value="usb">USB</option>
<option value="bd/dvd">BD/DVD</option>
<option value="game">GAME</option>
<option value="sat/catv">SAT/CATV</option>
<option value="video1">VIDEO 1</option>
<option value="video2">VIDEO 2</option>
<option value="tv">TV</option>
<option value="sa-cd/cd">SA-CD/CD</option>
<option value="network">Home Network</option>
<option value="cast">Chromecast</option>
</options>
</state>
</channel-type>
<channel-type id="input-dn1080-zone2">
<item-type>String</item-type>
<label>Input Source</label>
<description>Select the input source of the receiver</description>
<state>
<options>
<option value="btaudio">Bluetooth Audio</option>
<option value="fm">FM</option>
<option value="usb">USB</option>
<option value="source">SOURCE</option>
<option value="sat/catv">SAT/CATV</option>
<option value="video1">VIDEO 1</option>
<option value="sa-cd/cd">SA-CD/CD</option>
<option value="network">Home Network</option>
</options>
</state>
</channel-type>
<channel-type id="input-dn1080-zone4">
<item-type>String</item-type>
<label>Input Source</label>
<description>Select the input source of the receiver</description>
<state>
<options>
<option value="bd/dvd">BD/DVD</option>
<option value="game">GAME</option>
<option value="sat/catv">SAT/CATV</option>
<option value="video2">VIDEO 2</option>
<option value="sa-cd/cd">SA-CD/CD</option>
</options>
</state>
</channel-type>
<channel-type id="sound-field-dn1080">
<item-type>String</item-type>
<label>Sound Field</label>
<description>Select the Sound Field for the receiver</description>
<state>
<options>
<option value="pureDirect">Pure Direct</option>
<option value="2chStereo">2ch Stereo</option>
<option value="multiChStereo">Multi Ch Stereo</option>
<option value="direct">Direct</option>
<option value="audioFormatDecoding">A.F.D.</option>
<option value="dolbySurround">Dolby Surround</option>
<option value="neuralX">Neural:X</option>
<option value="frontSurround">Front Surround</option>
<option value="audioEnhancer">Audio Enhancer</option>
</options>
</state>
</channel-type>
<!-- STR-DN1080 Thing Type -->
<thing-type id="STR-DN1080">
<label>SONY Receiver STR-DN1080</label>
<description>SONY receiver STR-DN1080</description>
<channel-groups>
<channel-group typeId="masterControls" id="master"/>
<channel-group typeId="zone1Controls" id="zone1"/>
<channel-group typeId="zone2Controls" id="zone2"/>
<channel-group typeId="zone4Controls" id="zone4"/>
<channel-group typeId="radioControls" id="radio"/>
</channel-groups>
<config-description-ref uri="thing-type:sonyaudio:config"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="sonyaudio"
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">
<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>Set the volume level</description>
<category>SoundVolume</category>
<state min="0" max="100" step="1" pattern="%d %%"></state>
</channel-type>
<channel-type id="mute">
<item-type>Switch</item-type>
<label>Mute</label>
<description>Enable/Disable Mute on the AVR</description>
</channel-type>
</thing:thing-descriptions>