[vizio] Vizio TV binding - Initial contribution (#13309)

Signed-off-by: Michael Lobstein <michael.lobstein@gmail.com>
This commit is contained in:
mlobstein 2022-12-06 08:37:54 -06:00 committed by GitHub
parent a9d4244fd8
commit fc529777e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 3611 additions and 0 deletions

View File

@ -358,6 +358,7 @@
/bundles/org.openhab.binding.vesync/ @dag81 /bundles/org.openhab.binding.vesync/ @dag81
/bundles/org.openhab.binding.vigicrues/ @clinique /bundles/org.openhab.binding.vigicrues/ @clinique
/bundles/org.openhab.binding.vitotronic/ @steand /bundles/org.openhab.binding.vitotronic/ @steand
/bundles/org.openhab.binding.vizio/ @mlobstein
/bundles/org.openhab.binding.volvooncall/ @clinique @Jamstah /bundles/org.openhab.binding.volvooncall/ @clinique @Jamstah
/bundles/org.openhab.binding.warmup/ @jamesmelville /bundles/org.openhab.binding.warmup/ @jamesmelville
/bundles/org.openhab.binding.weathercompany/ @mhilbush /bundles/org.openhab.binding.weathercompany/ @mhilbush

View File

@ -1781,6 +1781,11 @@
<artifactId>org.openhab.binding.vitotronic</artifactId> <artifactId>org.openhab.binding.vitotronic</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.vizio</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.volvooncall</artifactId> <artifactId>org.openhab.binding.volvooncall</artifactId>

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,181 @@
# Vizio Binding
This binding connects Vizio TVs to openHAB.
The TV must support the Vizio SmartCast API that is found on 2016 and later models.
## Supported Things
There is currently only one supported thing type, which represents a Vizio TV using the `vizio_tv` id.
Multiple Things can be added if more than one Vizio TV is to be controlled.
## Discovery
Auto-discovery is supported if the Vizio TV can be located on the local network using mDNS.
Otherwise the thing must be manually added.
When the TV is discovered, a pairing process to obtain an authentication token from the TV must be completed using the openHAB console. See below for details.
## Thing Configuration
The thing has a few configuration parameters:
| Parameter | Description |
|-------------|--------------------------------------------------------------------------------------------------------------------------------------|
| hostName | The host name or IP address of the Vizio TV. Mandatory. |
| port | The port on the Vizio TV that listens for https connections. Default 7345; Use 9000 for older model TVs. |
| authToken | The token that is used to authenticate all commands sent to the TV. See below for instructions to obtain via the openHAB console. |
| appListJson | A JSON string that defines the apps that are available in the `activeApp` channel drop down. See below for instructions for editing. |
### Console Commands for Pairing:
To obtain an authorization token that enables openHAB to authenticate with the TV, the following console commands must be used while the TV is turned on.
The first command will send a pairing start request to the TV. This triggers the TV to display a 4-digit pairing code on screen that must be sent with the second command.
Start Pairing:
```
openhab:vizio <thingUID> start_pairing <deviceName>
```
Substitute `<thingUID>` with thing's id, ie: `vizio_tv:00bc3e711660`
Substitute `<deviceName>` the desired device name that will appear in the TV's settings, under Mobile Devices, ie: `Vizio-openHAB`
Submit Pairing Code:
```
openhab:vizio <thingUID> submit_code <pairingCode>
```
Substitute `<thingUID>` with the same thing id used above
Substitute `<pairingCode>` with the 4-digit pairing code displayed on the TV, ie: `1234`
The console should then indicate that pairing was successful (token will be displayed) and that the token was saved to the thing configuration.
If using file-based provisioning of the thing, the authorization token must be added to the thing configuration manually.
With an authorization token in place, the binding can now control the TV.
The authorization token text can be re-used in the event that it becomes necessary to setup the binding again.
By simply adding the token that is already recognized by the TV to the thing configuration, the pairing process can be bypassed.
## Channels
The following channels are available:
| Channel ID | Item Type | Description |
|-------------|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| power | Switch | Turn the power on the TV on or off. Note: TV may not turn on if power is switched off and the TV is configured for Eco mode. |
| volume | Dimmer | Control the volume on the TV (0-100%). |
| mute | Switch | Mute or unmute the volume on the TV. |
| source | String | Select the source input on the TV. The dropdown list is automatically populated from the TV. |
| activeApp | String | A dropdown containing a list of streaming apps defined by the `appListJson` config option that can be launched by the binding. An app started via remote control is automatically selected. |
| control | Player | Control Playback e.g. Play/Pause/Next/Previous/FForward/Rewind |
| button | String | Sends a remote control command the TV. See list of available commands below. |
### List of available button commands for Vizio TVs:
PowerOn
PowerOff
PowerToggle
VolumeUp
VolumeDown
MuteOn **(may only work as a toggle)**
MuteOff **(may only work as a toggle)**
MuteToggle
ChannelUp
ChannelDown
PreviousCh
InputToggle
SeekFwd
SeekBack
Play
Pause
Up
Down
Left
Right
Ok
Back
Info
Menu
Home
Exit
Smartcast
ccToggle
PictureMode
WideMode
WideToggle
### App List Configuration:
The Vizio API to launch and identify currently running apps on the TV is very complex.
To handle this, the binding maintains a JSON database of applications and their associated metadata in order to populate the `activeApp` dropdown with available apps.
When the thing is started for the first time, this JSON database is saved into the `appListJson` configuration parameter.
This list of apps can be edited via the script editor on the thing configuration.
By editing the JSON, apps that are not desired can be removed from the `activeApp` dropdown and newly discovered apps can be added.
An entry for an application has a `name` element and a `config` element containing `APP_ID`, `NAME_SPACE` and `MESSAGE` (null for most apps):
```
{
"name": "Crackle",
"config": {
"APP_ID": "5",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
```
If an app is running that is not currently recognized by the binding, the `activeApp` channel will display a message that contains the `APP_ID` and `NAME_SPACE` which can be used to create the missing record for that app in the JSON.
If an app that is in the JSON database fails to start when selected, try adjusting the `NAME_SPACE` value for that app.
`NAME_SPACE` seems to be a version number and adjusting the number up or down may correct the mismatch between the TV and the binding.
A current list of `APP_ID`'s can be found at http://hometest.buddytv.netdna-cdn.com/appservice/vizio_apps_prod.json
and `NAME_SPACE` &amp; `MESSAGE` values needed can be found at http://hometest.buddytv.netdna-cdn.com/appservice/app_availability_prod.json
If there is an error in the user supplied `appListJson`, the thing will fail to start and display a CONFIGURATION_PENDING message.
If all text in `appListJson` is removed (set to null) and the thing configuration saved, the binding will restore `appListJson` from the binding's JSON db.
## Full Example
vizio.things:
```
// Vizio TV
vizio:vizio_tv:mytv1 "My Vizio TV" [ hostName="192.168.10.1", port=7345, authToken="idspisp0pd" ]
```
vizio.items:
```
// Vizio TV items:
Switch TV_Power "Power" { channel="vizio:vizio_tv:mytv1:power" }
Dimmer TV_Volume "Volume [%d %%]" { channel="vizio:vizio_tv:mytv1:volume" }
Switch TV_Mute "Mute" { channel="vizio:vizio_tv:mytv1:mute" }
String TV_Source "Source Input [%s]" { channel="vizio:vizio_tv:mytv1:source" }
String TV_ActiveApp "Current App: [%s]" { channel="vizio:vizio_tv:mytv1:activeApp" }
Player TV_Control "Playback Control" { channel="vizio:vizio_tv:mytv1:control" }
String TV_Button "Send Command to TV" { channel="vizio:vizio_tv:mytv1:button" }
```
vizio.sitemap:
```
sitemap vizio label="Vizio" {
Frame label="My Vizio TV" {
Switch item=TV_Power
// Volume can be a Setpoint also
Slider item=TV_Volume minValue=0 maxValue=100 step=1 icon="soundvolume"
Switch item=TV_Mute icon="soundvolume_mute"
Selection item=TV_Source icon="screen"
Selection item=TV_ActiveApp icon="screen"
Default item=TV_Control
Selection item=TV_Button
}
}
```

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 https://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.4.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.vizio</artifactId>
<name>openHAB Add-ons :: Bundles :: Vizio Binding</name>
</project>

View File

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

View File

@ -0,0 +1,54 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal;
import java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link VizioBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Michael Lobstein - Initial contribution
*/
@NonNullByDefault
public class VizioBindingConstants {
public static final String BINDING_ID = "vizio";
public static final String PROPERTY_UUID = "uuid";
public static final String PROPERTY_HOST_NAME = "hostName";
public static final String PROPERTY_PORT = "port";
public static final String PROPERTY_AUTH_TOKEN = "authToken";
public static final String PROPERTY_APP_LIST_JSON = "appListJson";
public static final String EMPTY = "";
public static final String MODIFY_STRING_SETTING_JSON = "{\"REQUEST\": \"MODIFY\",\"VALUE\": \"%s\",\"HASHVAL\": %d}";
public static final String MODIFY_INT_SETTING_JSON = "{\"REQUEST\": \"MODIFY\",\"VALUE\": %d,\"HASHVAL\": %d}";
public static final String UNKNOWN_APP_STR = "Unknown app_id: %d, name_space: %d";
public static final String ON = "ON";
public static final String OFF = "OFF";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_VIZIO_TV = new ThingTypeUID(BINDING_ID, "vizio_tv");
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_VIZIO_TV);
// List of all Channel id's
public static final String POWER = "power";
public static final String VOLUME = "volume";
public static final String MUTE = "mute";
public static final String SOURCE = "source";
public static final String ACTIVE_APP = "activeApp";
public static final String CONTROL = "control";
public static final String BUTTON = "button";
}

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link VizioConfiguration} is the class used to match the
* thing configuration.
*
* @author Michael Lobstein - Initial contribution
*/
@NonNullByDefault
public class VizioConfiguration {
public @Nullable String hostName;
public Integer port = 7345;
public @Nullable String authToken;
public @Nullable String appListJson;
}

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link VizioException} extends Exception
*
* @author Michael Lobstein - Initial contribution
*/
@NonNullByDefault
public class VizioException extends Exception {
private static final long serialVersionUID = 1L;
public VizioException(String errorMessage) {
super(errorMessage);
}
}

View File

@ -0,0 +1,71 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal;
import static org.openhab.binding.vizio.internal.VizioBindingConstants.SUPPORTED_THING_TYPES_UIDS;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.vizio.internal.appdb.VizioAppDbService;
import org.openhab.binding.vizio.internal.handler.VizioHandler;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link VizioHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Michael Lobstein - Initial contribution
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.vizio")
public class VizioHandlerFactory extends BaseThingHandlerFactory {
private final HttpClient httpClient;
private final VizioStateDescriptionOptionProvider stateDescriptionProvider;
private final String vizioAppsJson;
@Activate
public VizioHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
final @Reference VizioStateDescriptionOptionProvider provider,
final @Reference VizioAppDbService vizioAppDbService) {
this.httpClient = httpClientFactory.getCommonHttpClient();
this.stateDescriptionProvider = provider;
this.vizioAppsJson = vizioAppDbService.getVizioAppsJson();
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) {
VizioHandler handler = new VizioHandler(thing, httpClient, stateDescriptionProvider, vizioAppsJson);
return handler;
}
return null;
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link VizioStateDescriptionOptionProvider} is a Dynamic provider of state options while leaving other state
* description fields as original.
*
* @author Michael Lobstein - Initial contribution
*/
@Component(service = { DynamicStateDescriptionProvider.class, VizioStateDescriptionOptionProvider.class })
@NonNullByDefault
public class VizioStateDescriptionOptionProvider extends BaseDynamicStateDescriptionProvider {
@Activate
public VizioStateDescriptionOptionProvider(final @Reference EventPublisher eventPublisher,
final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry,
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
this.eventPublisher = eventPublisher;
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
}
}

View File

@ -0,0 +1,57 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.appdb;
import static org.openhab.binding.vizio.internal.VizioBindingConstants.*;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link VizioAppDbService} class makes available a JSON list of known apps on Vizio TVs.
*
* @author Michael Lobstein - Initial Contribution
*/
@Component(service = VizioAppDbService.class)
@NonNullByDefault
public class VizioAppDbService {
private final Logger logger = LoggerFactory.getLogger(VizioAppDbService.class);
private String vizioAppsJson;
@Activate
public VizioAppDbService() {
try {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("/db/apps.json");
if (is != null) {
vizioAppsJson = new String(is.readAllBytes(), StandardCharsets.UTF_8);
} else {
vizioAppsJson = EMPTY;
}
} catch (IOException e) {
logger.warn("Unable to load Vizio app list : {}", e.getMessage());
vizioAppsJson = EMPTY;
}
}
public String getVizioAppsJson() {
return vizioAppsJson;
}
}

View File

@ -0,0 +1,293 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.communication;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.vizio.internal.VizioException;
import org.openhab.binding.vizio.internal.dto.PutResponse;
import org.openhab.binding.vizio.internal.dto.app.CurrentApp;
import org.openhab.binding.vizio.internal.dto.applist.VizioAppConfig;
import org.openhab.binding.vizio.internal.dto.audio.Audio;
import org.openhab.binding.vizio.internal.dto.input.CurrentInput;
import org.openhab.binding.vizio.internal.dto.inputlist.InputList;
import org.openhab.binding.vizio.internal.dto.pairing.PairingComplete;
import org.openhab.binding.vizio.internal.dto.pairing.PairingStart;
import org.openhab.binding.vizio.internal.dto.power.PowerMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
/**
* The {@link VizioCommunicator} class contains methods for accessing the HTTP interface of Vizio TVs
*
* @author Michael Lobstein - Initial contribution
*/
@NonNullByDefault
public class VizioCommunicator {
private final Logger logger = LoggerFactory.getLogger(VizioCommunicator.class);
private static final String AUTH_HEADER = "AUTH";
private static final String JSON_CONTENT_TYPE = "application/json";
private static final String JSON_VALUE = "{\"VALUE\": %s}";
private final HttpClient httpClient;
private final Gson gson = new GsonBuilder().serializeNulls().create();
private final String authToken;
private final String urlPowerMode;
private final String urlCurrentAudio;
private final String urlCurrentInput;
private final String urlInputList;
private final String urlChangeVolume;
private final String urlCurrentApp;
private final String urlLaunchApp;
private final String urlKeyPress;
private final String urlStartPairing;
private final String urlSubmitPairingCode;
public VizioCommunicator(HttpClient httpClient, String host, int port, String authToken) {
this.httpClient = httpClient;
this.authToken = authToken;
final String baseUrl = "https://" + host + ":" + port;
urlPowerMode = baseUrl + "/state/device/power_mode";
urlCurrentAudio = baseUrl + "/menu_native/dynamic/tv_settings/audio";
urlChangeVolume = baseUrl + "/menu_native/dynamic/tv_settings/audio/volume";
urlCurrentInput = baseUrl + "/menu_native/dynamic/tv_settings/devices/current_input";
urlInputList = baseUrl + "/menu_native/dynamic/tv_settings/devices/name_input";
urlCurrentApp = baseUrl + "/app/current";
urlLaunchApp = baseUrl + "/app/launch";
urlKeyPress = baseUrl + "/key_command/";
urlStartPairing = baseUrl + "/pairing/start";
urlSubmitPairingCode = baseUrl + "/pairing/pair";
}
/**
* Get the current power state of the Vizio TV
*
* @return A PowerMode response object
* @throws VizioException
*
*/
public PowerMode getPowerMode() throws VizioException {
return fromJson(getCommand(urlPowerMode), PowerMode.class);
}
/**
* Get the current audio settings of the Vizio TV
*
* @return An Audio response object
* @throws VizioException
*
*/
public Audio getCurrentAudioSettings() throws VizioException {
return fromJson(getCommand(urlCurrentAudio), Audio.class);
}
/**
* Change the volume of the Vizio TV
*
* @param the command JSON for the desired volue
* @return A PutResponse response object
* @throws VizioException
*
*/
public PutResponse changeVolume(String commandJSON) throws VizioException {
return fromJson(putCommand(urlChangeVolume, commandJSON), PutResponse.class);
}
/**
* Get the currently selected input of the Vizio TV
*
* @return A CurrentInput response object
* @throws VizioException
*
*/
public CurrentInput getCurrentInput() throws VizioException {
return fromJson(getCommand(urlCurrentInput), CurrentInput.class);
}
/**
* Change the currently selected input of the Vizio TV
*
* @param the command JSON for the selected input
* @return A PutResponse response object
* @throws VizioException
*
*/
public PutResponse changeInput(String commandJSON) throws VizioException {
return fromJson(putCommand(urlCurrentInput, commandJSON), PutResponse.class);
}
/**
* Get the list of available source inputs from the Vizio TV
*
* @return An InputList response object
* @throws VizioException
*
*/
public InputList getSourceInputList() throws VizioException {
return fromJson(getCommand(urlInputList), InputList.class);
}
/**
* Get the id of the app currently running on the Vizio TV
*
* @return A CurrentApp response object
* @throws VizioException
*
*/
public CurrentApp getCurrentApp() throws VizioException {
return fromJson(getCommand(urlCurrentApp), CurrentApp.class);
}
/**
* Launch a given streaming app on the Vizio TV
*
* @param the VizioAppConfig data for the app to launch
* @return A PutResponse response object
* @throws VizioException
*
*/
public PutResponse launchApp(VizioAppConfig appConfig) throws VizioException {
return fromJson(putCommand(urlLaunchApp, String.format(JSON_VALUE, gson.toJson(appConfig))), PutResponse.class);
}
/**
* Send a key press command to the Vizio TV
*
* @param the command JSON for the key press
* @return A PutResponse response object
* @throws VizioException
*
*/
public PutResponse sendKeyPress(String commandJSON) throws VizioException {
return fromJson(putCommand(urlKeyPress, commandJSON), PutResponse.class);
}
/**
* Start the pairing process to obtain an auth token from the TV
*
* @param the deviceName that is displayed in the TV settings after the device is registered
* @param the deviceId a unique number that identifies this pairing request
* @return A PairingStart response object
* @throws VizioException
*
*/
public PairingStart starPairing(String deviceName, int deviceId) throws VizioException {
return fromJson(
putCommand(urlStartPairing,
String.format("{ \"DEVICE_NAME\": \"%s\", \"DEVICE_ID\": \"%d\" }", deviceName, deviceId)),
PairingStart.class);
}
/**
* Finish the pairing process by submitting the code that was displayed on the TV to obtain the auth token
*
* @param the same deviceId that was used by startPairing()
* @param the pairingCode that was displayed on the TV
* @param the pairingToken returned by startPairing()
* @return A PairingComplete response object
* @throws VizioException
*
*/
public PairingComplete submitPairingCode(int deviceId, String pairingCode, int pairingToken) throws VizioException {
return fromJson(putCommand(urlSubmitPairingCode, String.format(
"{\"DEVICE_ID\": \"%d\",\"CHALLENGE_TYPE\": 1,\"RESPONSE_VALUE\": \"%s\",\"PAIRING_REQ_TOKEN\": %d}",
deviceId, pairingCode, pairingToken)), PairingComplete.class);
}
/**
* Sends a GET request to the Vizio TV
*
* @param url The url used to retrieve status information from the Vizio TV
* @return The response content of the http request
* @throws VizioException
*
*/
private String getCommand(String url) throws VizioException {
try {
final Request request = httpClient.newRequest(url).method(HttpMethod.GET);
request.header(AUTH_HEADER, authToken);
request.header(HttpHeader.CONTENT_TYPE, JSON_CONTENT_TYPE);
final ContentResponse response = request.send();
logger.trace("GET url: {}, response: {}", url, response.getContentAsString());
return response.getContentAsString();
} catch (InterruptedException | TimeoutException | ExecutionException e) {
throw new VizioException("Error executing vizio GET command, URL: " + url + " " + e.getMessage());
}
}
/**
* Sends a PUT request to the Vizio TV
*
* @param url The url used to send a command to the Vizio TV
* @param commandJSON The JSON data needed to execute the command
* @return The response content of the http request
* @throws VizioException
*
*/
private String putCommand(String url, String commandJSON) throws VizioException {
try {
final Request request = httpClient.newRequest(url).method(HttpMethod.PUT);
if (!url.contains("pairing")) {
request.header(AUTH_HEADER, authToken);
}
request.content(new StringContentProvider(commandJSON), JSON_CONTENT_TYPE);
final ContentResponse response = request.send();
logger.trace("PUT url: {}, response: {}", url, response.getContentAsString());
return response.getContentAsString();
} catch (InterruptedException | TimeoutException | ExecutionException e) {
throw new VizioException("Error executing vizio PUT command, URL: " + url + e.getMessage());
}
}
/**
* Wrapper for the Gson fromJson() method that encapsulates exception handling
*
* @param json The JSON string to be deserialized
* @param classOfT The type of class to be returned
* @return the deserialized object
* @throws VizioException
*
*/
private <T> T fromJson(String json, Class<T> classOfT) throws VizioException {
Object obj = null;
try {
obj = gson.fromJson(json, classOfT);
} catch (JsonSyntaxException e) {
throw new VizioException("Error Parsing JSON string: " + json + ", Exception: " + e.getMessage());
}
if (obj != null) {
return classOfT.cast(obj);
} else {
throw new VizioException("Error creating " + classOfT.getSimpleName() + " object for JSON string: " + json);
}
}
}

View File

@ -0,0 +1,59 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.communication;
import java.net.MalformedURLException;
import java.security.cert.CertificateException;
import javax.net.ssl.X509ExtendedTrustManager;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.io.net.http.PEMTrustManager;
import org.openhab.core.io.net.http.TlsTrustManagerProvider;
import org.openhab.core.io.net.http.TrustAllTrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provides a {@link PEMTrustManager} to allow secure connections to a Vizio TV that uses self signed
* certificates.
*
* @author Christoph Weitkamp - Initial Contribution
* @author Michael Lobstein - Adapted for Vizio binding
*/
@NonNullByDefault
public class VizioTlsTrustManagerProvider implements TlsTrustManagerProvider {
private final String hostname;
private final Logger logger = LoggerFactory.getLogger(VizioTlsTrustManagerProvider.class);
public VizioTlsTrustManagerProvider(String hostname) {
this.hostname = hostname;
}
@Override
public String getHostName() {
return hostname;
}
@Override
public X509ExtendedTrustManager getTrustManager() {
try {
logger.trace("Use self-signed certificate downloaded from Vizio TV.");
return PEMTrustManager.getInstanceFromServer("https://" + getHostName());
} catch (CertificateException | MalformedURLException e) {
logger.debug("An unexpected exception occurred - returning a TrustAllTrustManager: {}", e.getMessage(), e);
}
return TrustAllTrustManager.getInstance();
}
}

View File

@ -0,0 +1,183 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.console;
import static org.openhab.binding.vizio.internal.VizioBindingConstants.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.Random;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.vizio.internal.VizioException;
import org.openhab.binding.vizio.internal.communication.VizioCommunicator;
import org.openhab.binding.vizio.internal.dto.pairing.PairingComplete;
import org.openhab.binding.vizio.internal.handler.VizioHandler;
import org.openhab.core.io.console.Console;
import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingRegistry;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link VizioCommandExtension} is responsible for handling console commands
*
* @author Michael Lobstein - Initial contribution
*/
@NonNullByDefault
@Component(service = ConsoleCommandExtension.class)
public class VizioCommandExtension extends AbstractConsoleCommandExtension {
private static final String START_PAIRING = "start_pairing";
private static final String SUBMIT_CODE = "submit_code";
private final ThingRegistry thingRegistry;
private final HttpClient httpClient;
@Activate
public VizioCommandExtension(final @Reference ThingRegistry thingRegistry,
final @Reference HttpClientFactory httpClientFactory) {
super("vizio", "Interact with the Vizio binding to get an authentication token from the TV.");
this.thingRegistry = thingRegistry;
this.httpClient = httpClientFactory.getCommonHttpClient();
}
@Override
public void execute(String[] args, Console console) {
if (args.length == 3) {
Thing thing = null;
try {
ThingUID thingUID = new ThingUID(args[0]);
thing = thingRegistry.get(thingUID);
} catch (IllegalArgumentException e) {
thing = null;
}
ThingHandler thingHandler = null;
VizioHandler handler = null;
if (thing != null) {
thingHandler = thing.getHandler();
if (thingHandler instanceof VizioHandler) {
handler = (VizioHandler) thingHandler;
}
}
if (thing == null) {
console.println("Bad thing id '" + args[0] + "'");
printUsage(console);
} else if (thingHandler == null) {
console.println("No handler initialized for the thing id '" + args[0] + "'");
printUsage(console);
} else if (handler == null) {
console.println("'" + args[0] + "' is not a Vizio thing id");
printUsage(console);
} else {
String host = (String) thing.getConfiguration().get(PROPERTY_HOST_NAME);
BigDecimal port = (BigDecimal) thing.getConfiguration().get(PROPERTY_PORT);
if (host == null || host.isEmpty() || port.signum() < 1) {
console.println(
"Error! Host Name and Port must be specified in thing configuration before paring.");
return;
} else if (host.contains(":")) {
// format for ipv6
host = "[" + host + "]";
}
VizioCommunicator communicator = new VizioCommunicator(httpClient, host, port.intValue(), EMPTY);
switch (args[1]) {
case START_PAIRING:
try {
Random rng = new Random();
int pairingDeviceId = rng.nextInt(100000);
int pairingToken = communicator.starPairing(args[2], pairingDeviceId).getItem()
.getPairingReqToken();
if (pairingToken != -1) {
handler.setPairingDeviceId(pairingDeviceId);
handler.setPairingToken(pairingToken);
console.println("Pairing has been started!");
console.println(
"Please note the 4 digit code displayed on the TV and substitute it into the following console command:");
console.println(
"openhab:vizio " + handler.getThing().getUID() + " " + SUBMIT_CODE + " <NNNN>");
} else {
console.println("Unable to obtain pairing token!");
}
} catch (VizioException e) {
console.println("Error! Unable to start pairing process.");
console.println("Exception was: " + e.getMessage());
}
break;
case SUBMIT_CODE:
try {
int pairingDeviceId = handler.getPairingDeviceId();
int pairingToken = handler.getPairingToken();
if (pairingDeviceId < 0 || pairingToken < 0) {
console.println("Error! '" + START_PAIRING + "' command must be completed first.");
console.println(
"Please issue the following command and substitute the desired device name.");
console.println("openhab:vizio " + handler.getThing().getUID() + " " + START_PAIRING
+ " <deviceName>");
break;
}
Integer.valueOf(args[2]);
PairingComplete authTokenResp = communicator.submitPairingCode(pairingDeviceId, args[2],
pairingToken);
if (authTokenResp.getItem().getAuthToken() != EMPTY) {
console.println("Pairing complete!");
console.println("The auth token: " + authTokenResp.getItem().getAuthToken()
+ " was received and will be added to the thing configuration.");
console.println(
"If the thing is provisioned via a file, the token must be manually added to the thing configuration.");
handler.setPairingDeviceId(-1);
handler.setPairingToken(-1);
handler.saveAuthToken(authTokenResp.getItem().getAuthToken());
} else {
console.println("Unable to obtain auth token!");
}
} catch (NumberFormatException nfe) {
console.println(
"Error! Pairing code must be numeric. Check console command and try again.");
} catch (VizioException e) {
console.println("Error! Unable to complete pairing process.");
console.println("Exception was: " + e.getMessage());
}
break;
default:
printUsage(console);
break;
}
}
} else {
printUsage(console);
}
}
@Override
public List<String> getUsages() {
return List.of(new String[] {
buildCommandUsage("<thingUID> " + START_PAIRING + " <deviceName>", "start pairing process"),
buildCommandUsage("<thingUID> " + SUBMIT_CODE + " <pairingCode>", "submit pairing code") });
}
}

View File

@ -0,0 +1,132 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.discovery;
import static org.openhab.binding.vizio.internal.VizioBindingConstants.*;
import java.net.InetAddress;
import java.util.Set;
import javax.jmdns.ServiceInfo;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
import org.openhab.core.thing.Thing;
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;
/**
* The {@link VizioDiscoveryParticipant} is responsible processing the
* results of searches for mDNS services of type _viziocast._tcp.local.
*
* @author Michael Lobstein - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "discovery.vizio")
public class VizioDiscoveryParticipant implements MDNSDiscoveryParticipant {
private final Logger logger = LoggerFactory.getLogger(VizioDiscoveryParticipant.class);
@Override
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
return SUPPORTED_THING_TYPES_UIDS;
}
@Override
public String getServiceType() {
return "_viziocast._tcp.local.";
}
@Override
public @Nullable DiscoveryResult createResult(ServiceInfo service) {
DiscoveryResult result = null;
ThingUID thingUid = getThingUID(service);
if (thingUid != null) {
InetAddress ip = getIpAddress(service);
if (ip == null) {
return null;
}
String inetAddress = ip.toString().substring(1); // trim leading slash
String label = service.getName();
int port = service.getPort();
result = DiscoveryResultBuilder.create(thingUid).withLabel(label).withRepresentationProperty(PROPERTY_UUID)
.withProperty(PROPERTY_UUID, thingUid.getId())
.withProperty(Thing.PROPERTY_MODEL_ID, service.getPropertyString("mdl"))
.withProperty(PROPERTY_HOST_NAME, inetAddress).withProperty(PROPERTY_PORT, port).build();
logger.debug("Created {} for Vizio TV at {}, name: '{}'", result, inetAddress, label);
}
return result;
}
/**
* @see org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant#getThingUID(javax.jmdns.ServiceInfo)
*/
@Override
public @Nullable ThingUID getThingUID(ServiceInfo service) {
if (service.getType() != null && service.getType().equals(getServiceType())) {
String uidName = getUIDName(service);
return uidName != null ? new ThingUID(THING_TYPE_VIZIO_TV, uidName) : null;
}
return null;
}
/**
* Gets the UID name from the mdns record txt info (mac address), fall back with IP address
*
* @param service the mdns service
* @return the UID name
*/
private @Nullable String getUIDName(ServiceInfo service) {
String uid = service.getPropertyString("eth");
if (uid == null || uid.endsWith("000") || uid.length() < 12) {
uid = service.getPropertyString("wifi");
}
if (uid == null || uid.endsWith("000") || uid.length() < 12) {
InetAddress ip = getIpAddress(service);
if (ip == null) {
return null;
} else {
uid = ip.toString();
}
}
return uid.replaceAll("[^A-Za-z0-9_]", "_");
}
/**
* {@link InetAddress} gets the IP address of the device in v4 or v6 format.
*
* @param ServiceInfo service
* @return InetAddress the IP address
*
*/
private @Nullable InetAddress getIpAddress(ServiceInfo service) {
InetAddress address = null;
for (InetAddress addr : service.getInet4Addresses()) {
return addr;
}
// Fall back for Inet6addresses
for (InetAddress addr : service.getInet6Addresses()) {
return addr;
}
return address;
}
}

View File

@ -0,0 +1,93 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto;
import com.google.gson.annotations.SerializedName;
/**
* The {@link Item} class contains data from the Vizio TV JSON response
*
* @author Michael Lobstein - Initial contribution
*/
public class Item {
@SerializedName("HASHVAL")
private Long hashval;
@SerializedName("CNAME")
private String cname;
@SerializedName("NAME")
private String name = "";
@SerializedName("TYPE")
private String type;
@SerializedName("ENABLED")
private String enabled;
@SerializedName("READONLY")
private String readonly;
@SerializedName("VALUE")
private Value value = new Value();
public Long getHashval() {
return hashval;
}
public void setHashval(Long hashval) {
this.hashval = hashval;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getEnabled() {
return enabled;
}
public void setEnabled(String enabled) {
this.enabled = enabled;
}
public String getReadonly() {
return readonly;
}
public void setReadonly(String readonly) {
this.readonly = readonly;
}
public Value getValue() {
return value;
}
public void setValue(Value value) {
this.value = value;
}
}

View File

@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto;
import com.google.gson.annotations.SerializedName;
/**
* The {@link Parameters} class contains data from the Vizio TV JSON response
*
* @author Michael Lobstein - Initial contribution
*/
public class Parameters {
@SerializedName("FLAT")
private String flat;
@SerializedName("HELPTEXT")
private String helptext;
@SerializedName("HASHONLY")
private String hashonly;
public String getFlat() {
return flat;
}
public void setFlat(String flat) {
this.flat = flat;
}
public String getHelptext() {
return helptext;
}
public void setHelptext(String helptext) {
this.helptext = helptext;
}
public String getHashonly() {
return hashonly;
}
public void setHashonly(String hashonly) {
this.hashonly = hashonly;
}
}

View File

@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto;
import com.google.gson.annotations.SerializedName;
/**
* The {@link PutResponse} class maps the JSON data response from several Vizio TV endpoints
*
* @author Michael Lobstein - Initial contribution
*/
public class PutResponse {
@SerializedName("STATUS")
private Status status;
@SerializedName("URI")
private String uri;
@SerializedName("TIME")
private String time;
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto;
import com.google.gson.annotations.SerializedName;
/**
* The {@link Status} class contains data from the Vizio TV JSON response
*
* @author Michael Lobstein - Initial contribution
*/
public class Status {
@SerializedName("RESULT")
private String result;
@SerializedName("DETAIL")
private String detail;
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto;
import com.google.gson.annotations.SerializedName;
/**
* The {@link Value} class contains data from the Vizio TV JSON response
*
* @author Michael Lobstein - Initial contribution
*/
public class Value {
@SerializedName("NAME")
private String name = "";
@SerializedName("METADATA")
private String metadata;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMetadata() {
return metadata;
}
public void setMetadata(String metadata) {
this.metadata = metadata;
}
}

View File

@ -0,0 +1,56 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.app;
import org.openhab.binding.vizio.internal.dto.Status;
import com.google.gson.annotations.SerializedName;
/**
* The {@link CurrentApp} class maps the JSON data response from the Vizio TV endpoint:
* '/app/current'
*
* @author Michael Lobstein - Initial contribution
*/
public class CurrentApp {
@SerializedName("STATUS")
private Status status;
@SerializedName("ITEM")
private ItemApp item = new ItemApp();
@SerializedName("URI")
private String uri;
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public ItemApp getItem() {
return item;
}
public void setItem(ItemApp item) {
this.item = item;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
}

View File

@ -0,0 +1,44 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.app;
import com.google.gson.annotations.SerializedName;
/**
* The {@link ItemApp} class contains data from the Vizio TV JSON response
*
* @author Michael Lobstein - Initial contribution
*/
public class ItemApp {
@SerializedName("TYPE")
private String type;
@SerializedName("VALUE")
private ItemAppValue value = new ItemAppValue();
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public ItemAppValue getValue() {
return value;
}
public void setValue(ItemAppValue value) {
this.value = value;
}
}

View File

@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.app;
import com.google.gson.annotations.SerializedName;
/**
* The {@link ItemAppValue} class contains data from the Vizio TV JSON response
*
* @author Michael Lobstein - Initial contribution
*/
public class ItemAppValue {
@SerializedName("MESSAGE")
private String message;
@SerializedName("NAME_SPACE")
private Integer nameSpace = -1;
@SerializedName("APP_ID")
private String appId = "";
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Integer getNameSpace() {
return nameSpace;
}
public void setNameSpace(Integer nameSpace) {
this.nameSpace = nameSpace;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.applist;
import com.google.gson.annotations.SerializedName;
/**
* The {@link VizioApp} class contains the name and config data for an app that runs on a Vizio TV
*
* @author Michael Lobstein - Initial contribution
*/
public class VizioApp {
@SerializedName("name")
private String name = "";
@SerializedName("config")
private VizioAppConfig config = new VizioAppConfig();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public VizioAppConfig getConfig() {
return config;
}
public void setConfig(VizioAppConfig config) {
this.config = config;
}
}

View File

@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.applist;
import com.google.gson.annotations.SerializedName;
/**
* The {@link VizioAppConfig} class maps the JSON data needed to launch an app on a Vizio TV
*
* @author Michael Lobstein - Initial contribution
*/
public class VizioAppConfig {
@SerializedName("NAME_SPACE")
private Integer nameSpace;
@SerializedName("APP_ID")
private String appId;
@SerializedName("MESSAGE")
private String message;
public Integer getNameSpace() {
return nameSpace;
}
public void setNameSpace(Integer nameSpace) {
this.nameSpace = nameSpace;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -0,0 +1,36 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.applist;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.annotations.SerializedName;
/**
* The {@link VizioApps} class contains a list of VizioApp objects
*
* @author Michael Lobstein - Initial contribution
*/
public class VizioApps {
@SerializedName("Apps")
private List<VizioApp> apps = new ArrayList<VizioApp>();
public List<VizioApp> getApps() {
return apps;
}
public void setApps(List<VizioApp> apps) {
this.apps = apps;
}
}

View File

@ -0,0 +1,120 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.audio;
import java.util.ArrayList;
import java.util.List;
import org.openhab.binding.vizio.internal.dto.Parameters;
import org.openhab.binding.vizio.internal.dto.Status;
import com.google.gson.annotations.SerializedName;
/**
* The {@link Audio} class maps the JSON data response from the Vizio TV endpoint:
* '/menu_native/dynamic/tv_settings/audio'
*
* @author Michael Lobstein - Initial contribution
*/
public class Audio {
@SerializedName("STATUS")
private Status status;
@SerializedName("HASHLIST")
private List<Long> hashlist = new ArrayList<Long>();
@SerializedName("GROUP")
private String group;
@SerializedName("NAME")
private String name;
@SerializedName("PARAMETERS")
private Parameters parameters;
@SerializedName("ITEMS")
private List<ItemAudio> items = new ArrayList<ItemAudio>();
@SerializedName("URI")
private String uri;
@SerializedName("CNAME")
private String cname;
@SerializedName("TYPE")
private String type;
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public List<Long> getHashlist() {
return hashlist;
}
public void setHashlist(List<Long> hashlist) {
this.hashlist = hashlist;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Parameters getParameters() {
return parameters;
}
public void setParameters(Parameters parameters) {
this.parameters = parameters;
}
public List<ItemAudio> getItems() {
return items;
}
public void setItems(List<ItemAudio> items) {
this.items = items;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View File

@ -0,0 +1,93 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.audio;
import com.google.gson.annotations.SerializedName;
/**
* The {@link ItemAudio} class contains data from the Vizio TV JSON response
*
* @author Michael Lobstein - Initial contribution
*/
public class ItemAudio {
@SerializedName("HASHVAL")
private Long hashval = 0L;
@SerializedName("CNAME")
private String cname;
@SerializedName("NAME")
private String name;
@SerializedName("TYPE")
private String type;
@SerializedName("ENABLED")
private String enabled;
@SerializedName("READONLY")
private String readonly;
@SerializedName("VALUE")
private String value = "";
public Long getHashval() {
return hashval;
}
public void setHashval(Long hashval) {
this.hashval = hashval;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getEnabled() {
return enabled;
}
public void setEnabled(String enabled) {
this.enabled = enabled;
}
public String getReadonly() {
return readonly;
}
public void setReadonly(String readonly) {
this.readonly = readonly;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

View File

@ -0,0 +1,80 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.input;
import java.util.ArrayList;
import java.util.List;
import org.openhab.binding.vizio.internal.dto.Parameters;
import org.openhab.binding.vizio.internal.dto.Status;
import com.google.gson.annotations.SerializedName;
/**
* The {@link CurrentInput} class maps the JSON data response from the Vizio TV endpoint:
* '/menu_native/dynamic/tv_settings/devices/current_input'
*
* @author Michael Lobstein - Initial contribution
*/
public class CurrentInput {
@SerializedName("STATUS")
private Status status;
@SerializedName("ITEMS")
private List<ItemInput> items = new ArrayList<ItemInput>();
@SerializedName("HASHLIST")
private List<Long> hashlist = new ArrayList<Long>();
@SerializedName("URI")
private String uri;
@SerializedName("PARAMETERS")
private Parameters parameters;
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public List<ItemInput> getItems() {
return items;
}
public void setItems(List<ItemInput> items) {
this.items = items;
}
public List<Long> getHashlist() {
return hashlist;
}
public void setHashlist(List<Long> hashlist) {
this.hashlist = hashlist;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
public Parameters getParameters() {
return parameters;
}
public void setParameters(Parameters parameters) {
this.parameters = parameters;
}
}

View File

@ -0,0 +1,93 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.input;
import com.google.gson.annotations.SerializedName;
/**
* The {@link ItemInput} class contains data from the Vizio TV JSON response
*
* @author Michael Lobstein - Initial contribution
*/
public class ItemInput {
@SerializedName("HASHVAL")
private Long hashval = 0L;
@SerializedName("NAME")
private String name;
@SerializedName("ENABLED")
private String enabled;
@SerializedName("VALUE")
private String value = "";
@SerializedName("CNAME")
private String cname;
@SerializedName("HIDDEN")
private String hidden;
@SerializedName("TYPE")
private String type;
public Long getHashval() {
return hashval;
}
public void setHashval(Long hashval) {
this.hashval = hashval;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEnabled() {
return enabled;
}
public void setEnabled(String enabled) {
this.enabled = enabled;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public String getHidden() {
return hidden;
}
public void setHidden(String hidden) {
this.hidden = hidden;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View File

@ -0,0 +1,121 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.inputlist;
import java.util.ArrayList;
import java.util.List;
import org.openhab.binding.vizio.internal.dto.Item;
import org.openhab.binding.vizio.internal.dto.Parameters;
import org.openhab.binding.vizio.internal.dto.Status;
import com.google.gson.annotations.SerializedName;
/**
* The {@link InputList} class maps the JSON data response from the Vizio TV endpoint:
* '/menu_native/dynamic/tv_settings/devices/name_input'
*
* @author Michael Lobstein - Initial contribution
*/
public class InputList {
@SerializedName("STATUS")
private Status status;
@SerializedName("HASHLIST")
private List<Long> hashlist = new ArrayList<Long>();
@SerializedName("GROUP")
private String group;
@SerializedName("NAME")
private String name;
@SerializedName("PARAMETERS")
private Parameters parameters;
@SerializedName("ITEMS")
private List<Item> items = new ArrayList<Item>();
@SerializedName("URI")
private String uri;
@SerializedName("CNAME")
private String cname;
@SerializedName("TYPE")
private String type;
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public List<Long> getHashlist() {
return hashlist;
}
public void setHashlist(List<Long> hashlist) {
this.hashlist = hashlist;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Parameters getParameters() {
return parameters;
}
public void setParameters(Parameters parameters) {
this.parameters = parameters;
}
public List<Item> getItems() {
return items;
}
public void setItems(List<Item> items) {
this.items = items;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View File

@ -0,0 +1,33 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.pairing;
import com.google.gson.annotations.SerializedName;
/**
* The {@link ItemAuthToken} class contains data from the Vizio TV in response to completing the pairing process
*
* @author Michael Lobstein - Initial contribution
*/
public class ItemAuthToken {
@SerializedName("AUTH_TOKEN")
private String authToken = "";
public String getAuthToken() {
return authToken;
}
public void setAuthToken(String authToken) {
this.authToken = authToken;
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.pairing;
import com.google.gson.annotations.SerializedName;
/**
* The {@link ItemPairing} class contains data from the Vizio TV in response to starting the pairing process
*
* @author Michael Lobstein - Initial contribution
*/
public class ItemPairing {
@SerializedName("PAIRING_REQ_TOKEN")
private Integer pairingReqToken = -1;
@SerializedName("CHALLENGE_TYPE")
private Integer challengeType = -1;
public Integer getPairingReqToken() {
return pairingReqToken;
}
public void setPairingReqToken(Integer pairingReqToken) {
this.pairingReqToken = pairingReqToken;
}
public Integer getChallengeType() {
return challengeType;
}
public void setChallengeType(Integer challengeType) {
this.challengeType = challengeType;
}
}

View File

@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.pairing;
import com.google.gson.annotations.SerializedName;
/**
* The {@link PairingComplete} class maps the JSON data response from the Vizio TV endpoint:
* '/pairing/pair'
*
* @author Michael Lobstein - Initial contribution
*/
public class PairingComplete {
@SerializedName("ITEM")
private ItemAuthToken item = new ItemAuthToken();
public ItemAuthToken getItem() {
return item;
}
public void setItem(ItemAuthToken item) {
this.item = item;
}
}

View File

@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.pairing;
import com.google.gson.annotations.SerializedName;
/**
* The {@link PairingStart} class maps the JSON data response from the Vizio TV endpoint:
* '/pairing/start'
*
* @author Michael Lobstein - Initial contribution
*/
public class PairingStart {
@SerializedName("ITEM")
private ItemPairing item = new ItemPairing();
public ItemPairing getItem() {
return item;
}
public void setItem(ItemPairing item) {
this.item = item;
}
}

View File

@ -0,0 +1,63 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.power;
import com.google.gson.annotations.SerializedName;
/**
* The {@link ItemPower} class contains data from the Vizio TV JSON response
*
* @author Michael Lobstein - Initial contribution
*/
public class ItemPower {
@SerializedName("CNAME")
private String cname;
@SerializedName("TYPE")
private String type;
@SerializedName("NAME")
private String name;
@SerializedName("VALUE")
private int value;
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}

View File

@ -0,0 +1,59 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.dto.power;
import java.util.ArrayList;
import java.util.List;
import org.openhab.binding.vizio.internal.dto.Status;
import com.google.gson.annotations.SerializedName;
/**
* The {@link PowerMode} class maps the JSON data response from the Vizio TV endpoint:
* '/state/device/power_mode'
*
* @author Michael Lobstein - Initial contribution
*/
public class PowerMode {
@SerializedName("STATUS")
private Status status;
@SerializedName("ITEMS")
private List<ItemPower> items = new ArrayList<ItemPower>();
@SerializedName("URI")
private String uri;
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public List<ItemPower> getItems() {
return items;
}
public void setItems(List<ItemPower> items) {
this.items = items;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
}

View File

@ -0,0 +1,70 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.enums;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link KeyCommand} class provides enum values for remote control button press commands.
*
* @author Michael Lobstein - Initial contribution
*/
@NonNullByDefault
public enum KeyCommand {
SEEKFWD(2, 0),
SEEKBACK(2, 1),
PAUSE(2, 2),
PLAY(2, 3),
DOWN(3, 0),
LEFT(3, 1),
OK(3, 2),
LEFT2(3, 4),
RIGHT(3, 7),
UP(3, 8),
BACK(4, 0),
SMARTCAST(4, 3),
CCTOGGLE(4, 4),
INFO(4, 6),
MENU(4, 8),
HOME(4, 15),
VOLUMEDOWN(5, 0),
VOLUMEUP(5, 1),
MUTEOFF(5, 2),
MUTEON(5, 3),
MUTETOGGLE(5, 4),
PICTUREMODE(6, 0),
WIDEMODE(6, 1),
WIDETOGGLE(6, 2),
INPUTTOGGLE(7, 1),
CHANNELDOWN(8, 0),
CHANNELUP(8, 1),
PREVIOUSCH(8, 2),
EXIT(9, 0),
POWEROFF(11, 0),
POWERON(11, 1),
POWERTOGGLE(11, 2);
private static final String KEY_COMMAND_STR = "{\"KEYLIST\": [{\"CODESET\": %d,\"CODE\": %d,\"ACTION\":\"KEYPRESS\"}]}";
private final int codeSet;
private final int code;
KeyCommand(int codeSet, int code) {
this.codeSet = codeSet;
this.code = code;
}
public String getJson() {
return String.format(KEY_COMMAND_STR, codeSet, code);
}
}

View File

@ -0,0 +1,584 @@
/**
* Copyright (c) 2010-2022 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.vizio.internal.handler;
import static org.openhab.binding.vizio.internal.VizioBindingConstants.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.vizio.internal.VizioConfiguration;
import org.openhab.binding.vizio.internal.VizioException;
import org.openhab.binding.vizio.internal.VizioStateDescriptionOptionProvider;
import org.openhab.binding.vizio.internal.communication.VizioCommunicator;
import org.openhab.binding.vizio.internal.communication.VizioTlsTrustManagerProvider;
import org.openhab.binding.vizio.internal.dto.app.CurrentApp;
import org.openhab.binding.vizio.internal.dto.applist.VizioApp;
import org.openhab.binding.vizio.internal.dto.applist.VizioApps;
import org.openhab.binding.vizio.internal.dto.audio.Audio;
import org.openhab.binding.vizio.internal.dto.audio.ItemAudio;
import org.openhab.binding.vizio.internal.dto.input.CurrentInput;
import org.openhab.binding.vizio.internal.dto.inputlist.InputList;
import org.openhab.binding.vizio.internal.dto.power.PowerMode;
import org.openhab.binding.vizio.internal.enums.KeyCommand;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.io.net.http.TlsTrustManagerProvider;
import org.openhab.core.library.types.NextPreviousType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.PlayPauseType;
import org.openhab.core.library.types.RewindFastforwardType;
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.openhab.core.types.StateOption;
import org.openhab.core.types.UnDefType;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
/**
* The {@link VizioHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Michael Lobstein - Initial contribution
*/
@NonNullByDefault
public class VizioHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(VizioHandler.class);
private final HttpClient httpClient;
private final VizioStateDescriptionOptionProvider stateDescriptionProvider;
private final String dbAppsJson;
private @Nullable ServiceRegistration<?> serviceRegistration;
private @Nullable ScheduledFuture<?> refreshJob;
private @Nullable ScheduledFuture<?> metadataRefreshJob;
private VizioCommunicator communicator;
private List<VizioApp> userConfigApps = new ArrayList<VizioApp>();
private Object sequenceLock = new Object();
private int pairingDeviceId = -1;
private int pairingToken = -1;
private Long currentInputHash = 0L;
private Long currentVolumeHash = 0L;
private String currentApp = EMPTY;
private String currentInput = EMPTY;
private boolean currentMute = false;
private int currentVolume = -1;
private boolean powerOn = false;
private boolean debounce = true;
public VizioHandler(Thing thing, HttpClient httpClient,
VizioStateDescriptionOptionProvider stateDescriptionProvider, String vizioAppsJson) {
super(thing);
this.httpClient = httpClient;
this.stateDescriptionProvider = stateDescriptionProvider;
this.dbAppsJson = vizioAppsJson;
this.communicator = new VizioCommunicator(httpClient, EMPTY, -1, EMPTY);
}
@Override
public void initialize() {
logger.debug("Initializing Vizio handler");
final Gson gson = new Gson();
VizioConfiguration config = getConfigAs(VizioConfiguration.class);
@Nullable
String host = config.hostName;
final @Nullable String authToken = config.authToken;
@Nullable
String appListJson = config.appListJson;
if (host == null || host.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.configuration-error-hostname");
return;
} else if (host.contains(":")) {
// format for ipv6
host = "[" + host + "]";
}
this.communicator = new VizioCommunicator(httpClient, host, config.port, authToken != null ? authToken : EMPTY);
// register trustmanager service to allow httpClient to accept self signed cert from the Vizio TV
VizioTlsTrustManagerProvider tlsTrustManagerProvider = new VizioTlsTrustManagerProvider(
host + ":" + config.port);
serviceRegistration = FrameworkUtil.getBundle(getClass()).getBundleContext()
.registerService(TlsTrustManagerProvider.class.getName(), tlsTrustManagerProvider, null);
if (authToken == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING,
"@text/offline.configuration-error-authtoken");
return;
}
// if app list is not supplied in thing configuration, populate it from the json db
if (appListJson == null) {
appListJson = dbAppsJson;
// Update thing configuration (persistent) - store app list from db into thing so the user can update it
Configuration configuration = this.getConfig();
configuration.put(PROPERTY_APP_LIST_JSON, appListJson);
this.updateConfiguration(configuration);
}
try {
VizioApps appsFromJson = gson.fromJson(appListJson, VizioApps.class);
if (appsFromJson != null && !appsFromJson.getApps().isEmpty()) {
userConfigApps = appsFromJson.getApps();
List<StateOption> appListOptions = new ArrayList<>();
userConfigApps.forEach(app -> {
appListOptions.add(new StateOption(app.getName(), app.getName()));
});
stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), ACTIVE_APP),
appListOptions);
}
} catch (JsonSyntaxException e) {
logger.debug("Invalid App List Configuration in thing configuration. Exception: {}", e.getMessage(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.configuration-error-applist");
return;
}
updateStatus(ThingStatus.UNKNOWN);
startVizioStateRefresh();
startPeriodicRefresh();
}
/**
* Start the job that queries the Vizio TV every 10 seconds to get its current status
*/
private void startVizioStateRefresh() {
ScheduledFuture<?> refreshJob = this.refreshJob;
if (refreshJob == null || refreshJob.isCancelled()) {
this.refreshJob = scheduler.scheduleWithFixedDelay(this::refreshVizioState, 5, 10, TimeUnit.SECONDS);
}
}
/**
* Get current status from the Vizio TV and update the channels
*/
private void refreshVizioState() {
synchronized (sequenceLock) {
try {
PowerMode polledPowerMode = communicator.getPowerMode();
if (debounce && !polledPowerMode.getItems().isEmpty()) {
int powerMode = polledPowerMode.getItems().get(0).getValue();
if (powerMode == 1) {
powerOn = true;
updateState(POWER, OnOffType.ON);
} else if (powerMode == 0) {
powerOn = false;
updateState(POWER, OnOffType.OFF);
} else {
logger.debug("Unknown power mode {}, for response object: {}", powerMode, polledPowerMode);
}
}
updateStatus(ThingStatus.ONLINE);
} catch (VizioException e) {
logger.debug("Unable to retrieve Vizio TV power mode info. Exception: {}", e.getMessage(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.communication-error-get-power");
}
if (powerOn && (isLinked(VOLUME) || isLinked(MUTE))) {
try {
Audio audioSettings = communicator.getCurrentAudioSettings();
Optional<ItemAudio> volumeItem = audioSettings.getItems().stream()
.filter(i -> VOLUME.equals(i.getCname())).findFirst();
if (debounce && volumeItem.isPresent()) {
currentVolumeHash = volumeItem.get().getHashval();
try {
int polledVolume = Integer.parseInt(volumeItem.get().getValue());
if (polledVolume != currentVolume) {
currentVolume = polledVolume;
updateState(VOLUME, new PercentType(BigDecimal.valueOf(currentVolume)));
}
} catch (NumberFormatException e) {
logger.debug("Unable to parse volume value {} as int", volumeItem.get().getValue());
}
}
Optional<ItemAudio> muteItem = audioSettings.getItems().stream()
.filter(i -> MUTE.equals(i.getCname())).findFirst();
if (debounce && muteItem.isPresent()) {
String polledMute = muteItem.get().getValue().toUpperCase(Locale.ENGLISH);
if (ON.equals(polledMute) || OFF.equals(polledMute)) {
if (ON.equals(polledMute) && !currentMute) {
updateState(MUTE, OnOffType.ON);
currentMute = true;
} else if (OFF.equals(polledMute) && currentMute) {
updateState(MUTE, OnOffType.OFF);
currentMute = false;
}
} else {
logger.debug("Unknown mute mode {}, for response object: {}", polledMute, audioSettings);
}
}
} catch (VizioException e) {
logger.debug("Unable to retrieve Vizio TV current audio settings. Exception: {}", e.getMessage(),
e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.communication-error-get-audio");
}
}
if (powerOn && isLinked(SOURCE)) {
try {
CurrentInput polledInputState = communicator.getCurrentInput();
if (debounce && !polledInputState.getItems().isEmpty()
&& !currentInput.equals(polledInputState.getItems().get(0).getValue())) {
currentInput = polledInputState.getItems().get(0).getValue();
currentInputHash = polledInputState.getItems().get(0).getHashval();
updateState(SOURCE, new StringType(currentInput));
}
} catch (VizioException e) {
logger.debug("Unable to retrieve Vizio TV current input. Exception: {}", e.getMessage(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.communication-error-get-input");
}
}
if (powerOn && isLinked(ACTIVE_APP)) {
try {
if (debounce) {
CurrentApp polledApp = communicator.getCurrentApp();
Optional<VizioApp> currentAppData = userConfigApps.stream()
.filter(a -> a.getConfig().getAppId().equals(polledApp.getItem().getValue().getAppId())
&& a.getConfig().getNameSpace()
.equals(polledApp.getItem().getValue().getNameSpace()))
.findFirst();
if (currentAppData.isPresent()) {
if (!currentApp.equals(currentAppData.get().getName())) {
currentApp = currentAppData.get().getName();
updateState(ACTIVE_APP, new StringType(currentApp));
}
} else {
currentApp = EMPTY;
try {
int appId = Integer.parseInt(polledApp.getItem().getValue().getAppId());
updateState(ACTIVE_APP, new StringType(String.format(UNKNOWN_APP_STR, appId,
polledApp.getItem().getValue().getNameSpace())));
} catch (NumberFormatException nfe) {
// Non-numeric appId received, eg: hdmi1
updateState(ACTIVE_APP, UnDefType.UNDEF);
}
logger.debug("Unknown app_id: {}, name_space: {}",
polledApp.getItem().getValue().getAppId(),
polledApp.getItem().getValue().getNameSpace());
}
}
} catch (VizioException e) {
logger.debug("Unable to retrieve Vizio TV current running app. Exception: {}", e.getMessage(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.communication-error-get-app");
}
}
}
debounce = true;
}
/**
* Start the job to periodically retrieve various metadata from the Vizio TV every 10 minutes
*/
private void startPeriodicRefresh() {
ScheduledFuture<?> metadataRefreshJob = this.metadataRefreshJob;
if (metadataRefreshJob == null || metadataRefreshJob.isCancelled()) {
this.metadataRefreshJob = scheduler.scheduleWithFixedDelay(this::refreshVizioMetadata, 1, 600,
TimeUnit.SECONDS);
}
}
/**
* Update source list (hashes) and other metadata from the Vizio TV
*/
private void refreshVizioMetadata() {
synchronized (sequenceLock) {
try {
InputList inputList = communicator.getSourceInputList();
List<StateOption> sourceListOptions = new ArrayList<>();
inputList.getItems().forEach(source -> {
sourceListOptions.add(new StateOption(source.getName(), source.getValue().getName()));
});
stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), SOURCE),
sourceListOptions);
} catch (VizioException e) {
logger.debug("Unable to retrieve the Vizio TV input list. Exception: {}", e.getMessage(), e);
}
}
}
@Override
public void dispose() {
ScheduledFuture<?> refreshJob = this.refreshJob;
if (refreshJob != null) {
refreshJob.cancel(true);
this.refreshJob = null;
}
ScheduledFuture<?> metadataRefreshJob = this.metadataRefreshJob;
if (metadataRefreshJob != null) {
metadataRefreshJob.cancel(true);
this.metadataRefreshJob = null;
}
ServiceRegistration<?> localServiceRegistration = serviceRegistration;
if (localServiceRegistration != null) {
// remove trustmanager service
localServiceRegistration.unregister();
serviceRegistration = null;
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
logger.debug("Unsupported refresh command: {}", command);
} else {
switch (channelUID.getId()) {
case POWER:
debounce = false;
synchronized (sequenceLock) {
try {
if (command == OnOffType.ON) {
communicator.sendKeyPress(KeyCommand.POWERON.getJson());
powerOn = true;
} else {
communicator.sendKeyPress(KeyCommand.POWEROFF.getJson());
powerOn = false;
}
} catch (VizioException e) {
logger.debug("Unable to send power {} command to the Vizio TV, Exception: {}", command,
e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.communication-error-set-power");
}
}
break;
case VOLUME:
debounce = false;
synchronized (sequenceLock) {
try {
int volume = Integer.parseInt(command.toString());
// volume changed again before polling has run, get current volume hash from the TV first
if (currentVolumeHash.equals(0L)) {
Audio audioSettings = communicator.getCurrentAudioSettings();
Optional<ItemAudio> volumeItem = audioSettings.getItems().stream()
.filter(i -> VOLUME.equals(i.getCname())).findFirst();
if (volumeItem.isPresent()) {
currentVolumeHash = volumeItem.get().getHashval();
} else {
logger.debug("Unable to get current volume hash on the Vizio TV");
}
}
communicator
.changeVolume(String.format(MODIFY_INT_SETTING_JSON, volume, currentVolumeHash));
currentVolumeHash = 0L;
} catch (VizioException e) {
logger.debug("Unable to set volume on the Vizio TV, command volume: {}, Exception: {}",
command, e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.communication-error-set-volume");
} catch (NumberFormatException e) {
logger.debug("Unable to parse command volume value {} as int", command);
}
}
break;
case MUTE:
debounce = false;
synchronized (sequenceLock) {
try {
if (command == OnOffType.ON && !currentMute) {
communicator.sendKeyPress(KeyCommand.MUTETOGGLE.getJson());
currentMute = true;
} else if (command == OnOffType.OFF && currentMute) {
communicator.sendKeyPress(KeyCommand.MUTETOGGLE.getJson());
currentMute = false;
}
} catch (VizioException e) {
logger.debug("Unable to send mute {} command to the Vizio TV, Exception: {}", command,
e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.communication-error-set-mute");
}
}
break;
case SOURCE:
debounce = false;
synchronized (sequenceLock) {
try {
// if input changed again before polling has run, get current input hash from the TV
// first
if (currentInputHash.equals(0L)) {
CurrentInput polledInput = communicator.getCurrentInput();
if (!polledInput.getItems().isEmpty()) {
currentInputHash = polledInput.getItems().get(0).getHashval();
}
}
communicator
.changeInput(String.format(MODIFY_STRING_SETTING_JSON, command, currentInputHash));
currentInputHash = 0L;
} catch (VizioException e) {
logger.debug("Unable to set current source on the Vizio TV, source: {}, Exception: {}",
command, e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.communication-error-set-source");
}
}
break;
case ACTIVE_APP:
debounce = false;
synchronized (sequenceLock) {
try {
Optional<VizioApp> selectedApp = userConfigApps.stream()
.filter(a -> command.toString().equals(a.getName())).findFirst();
if (selectedApp.isPresent()) {
communicator.launchApp(selectedApp.get().getConfig());
} else {
logger.debug("Unknown app name: '{}', check that it exists in App List configuration",
command);
}
} catch (VizioException e) {
logger.debug("Unable to launch app name: '{}' on the Vizio TV, Exception: {}", command,
e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.communication-error-launch-app");
}
}
break;
case CONTROL:
debounce = false;
synchronized (sequenceLock) {
try {
handleControlCommand(command);
} catch (VizioException e) {
logger.debug("Unable to send control command: '{}' to the Vizio TV, Exception: {}", command,
e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.communication-error-send-cmd");
}
}
break;
case BUTTON:
synchronized (sequenceLock) {
try {
KeyCommand keyCommand = KeyCommand.valueOf(command.toString().toUpperCase(Locale.ENGLISH));
communicator.sendKeyPress(keyCommand.getJson());
} catch (IllegalArgumentException | VizioException e) {
logger.debug("Unable to send keypress to the Vizio TV, key: {}, Exception: {}", command,
e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.communication-error-send-key");
}
}
break;
default:
logger.warn("Unknown channel: '{}'", channelUID.getId());
break;
}
}
}
private void handleControlCommand(Command command) throws VizioException {
if (command instanceof PlayPauseType) {
if (command == PlayPauseType.PLAY) {
communicator.sendKeyPress(KeyCommand.PLAY.getJson());
} else if (command == PlayPauseType.PAUSE) {
communicator.sendKeyPress(KeyCommand.PAUSE.getJson());
}
} else if (command instanceof NextPreviousType) {
if (command == NextPreviousType.NEXT) {
communicator.sendKeyPress(KeyCommand.RIGHT.getJson());
} else if (command == NextPreviousType.PREVIOUS) {
communicator.sendKeyPress(KeyCommand.LEFT.getJson());
}
} else if (command instanceof RewindFastforwardType) {
if (command == RewindFastforwardType.FASTFORWARD) {
communicator.sendKeyPress(KeyCommand.SEEKFWD.getJson());
} else if (command == RewindFastforwardType.REWIND) {
communicator.sendKeyPress(KeyCommand.SEEKBACK.getJson());
}
} else {
logger.warn("Unknown control command: {}", command);
}
}
@Override
public boolean isLinked(String channelName) {
Channel channel = this.thing.getChannel(channelName);
if (channel != null) {
return isLinked(channel.getUID());
} else {
return false;
}
}
// The remaining methods are used by the console when obtaining the auth token from the TV.
public void saveAuthToken(String authToken) {
// Store the auth token in the configuration and restart the thing
Configuration configuration = this.getConfig();
configuration.put(PROPERTY_AUTH_TOKEN, authToken);
this.updateConfiguration(configuration);
this.thingUpdated(this.getThing());
}
public int getPairingDeviceId() {
return pairingDeviceId;
}
public void setPairingDeviceId(int pairingDeviceId) {
this.pairingDeviceId = pairingDeviceId;
}
public int getPairingToken() {
return pairingToken;
}
public void setPairingToken(int pairingToken) {
this.pairingToken = pairingToken;
}
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="vizio" 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>Vizio Binding</name>
<description>Controls Vizio TVs w/SmartCast API (2016+ Models)</description>
</binding:binding>

View File

@ -0,0 +1,81 @@
# binding
binding.vizio.name = Vizio Binding
binding.vizio.description = Controls Vizio TVs w/SmartCast API (2016+ Models)
# thing types
thing-type.vizio.vizio_tv.label = Vizio TV
thing-type.vizio.vizio_tv.description = A Vizio SmartCast TV
# thing types config
thing-type.config.vizio.vizio_tv.appListJson.label = App List Configuration
thing-type.config.vizio.vizio_tv.appListJson.description = The JSON configuration string for the list of apps available in the activeApp channel drop down
thing-type.config.vizio.vizio_tv.authToken.label = Auth Token
thing-type.config.vizio.vizio_tv.authToken.description = Auth Token that is obtained via the pairing process; See documentation for details
thing-type.config.vizio.vizio_tv.hostName.label = Host Name/IP Address
thing-type.config.vizio.vizio_tv.hostName.description = Host Name or IP Address of the Vizio TV
thing-type.config.vizio.vizio_tv.port.label = Port
thing-type.config.vizio.vizio_tv.port.description = Port for the Vizio TV
thing-type.config.vizio.vizio_tv.port.option.7345 = 7345 (Newer Models)
thing-type.config.vizio.vizio_tv.port.option.9000 = 9000 (Older Models)
# channel types
channel-type.vizio.activeApp.label = Active App
channel-type.vizio.activeApp.description = The currently running App on the TV
channel-type.vizio.buttonTv.label = Remote Button
channel-type.vizio.buttonTv.description = A Remote Button press to send to the TV
channel-type.vizio.buttonTv.state.option.PowerOn = Power On
channel-type.vizio.buttonTv.state.option.PowerOff = Power Off
channel-type.vizio.buttonTv.state.option.PowerToggle = Power Toggle
channel-type.vizio.buttonTv.state.option.VolumeUp = Volume Up
channel-type.vizio.buttonTv.state.option.VolumeDown = Volume Down
channel-type.vizio.buttonTv.state.option.MuteOn = Mute On
channel-type.vizio.buttonTv.state.option.MuteOff = Mute Off
channel-type.vizio.buttonTv.state.option.MuteToggle = Mute Toggle
channel-type.vizio.buttonTv.state.option.ChannelUp = Channel Up
channel-type.vizio.buttonTv.state.option.ChannelDown = Channel Down
channel-type.vizio.buttonTv.state.option.PreviousCh = Previous Channel
channel-type.vizio.buttonTv.state.option.InputToggle = Input Toggle
channel-type.vizio.buttonTv.state.option.SeekFwd = Seek Fwd
channel-type.vizio.buttonTv.state.option.SeekBack = Seek Back
channel-type.vizio.buttonTv.state.option.Play = Play
channel-type.vizio.buttonTv.state.option.Pause = Pause
channel-type.vizio.buttonTv.state.option.Up = Up
channel-type.vizio.buttonTv.state.option.Down = Down
channel-type.vizio.buttonTv.state.option.Left = Left
channel-type.vizio.buttonTv.state.option.Right = Right
channel-type.vizio.buttonTv.state.option.Ok = Ok
channel-type.vizio.buttonTv.state.option.Back = Back
channel-type.vizio.buttonTv.state.option.Info = Info
channel-type.vizio.buttonTv.state.option.Menu = Menu
channel-type.vizio.buttonTv.state.option.Home = Home
channel-type.vizio.buttonTv.state.option.Exit = Exit
channel-type.vizio.buttonTv.state.option.Smartcast = Smartcast
channel-type.vizio.buttonTv.state.option.ccToggle = CC Toggle
channel-type.vizio.buttonTv.state.option.PictureMode = Picture Mode
channel-type.vizio.buttonTv.state.option.WideMode = Wide Mode
channel-type.vizio.buttonTv.state.option.WideToggle = Wide Toggle
channel-type.vizio.control.label = Control
channel-type.vizio.control.description = Transport Controls e.g. Play/Pause/Next/Previous/FForward/Rewind
channel-type.vizio.source.label = Source Input
channel-type.vizio.source.description = Select the Source Input for the TV
# message strings
offline.configuration-error-hostname = Host Name must be specified
offline.configuration-error-authtoken = Auth Token must be specified, see documentation for details
offline.configuration-error-applist = Invalid App List Configuration in thing configuration
offline.communication-error-get-power = Unable to retrieve power mode info from TV
offline.communication-error-get-audio = Unable to retrieve current audio settings from TV
offline.communication-error-get-input = Unable to retrieve current input from TV
offline.communication-error-get-app = Unable to retrieve current running app from TV
offline.communication-error-set-power = Unable to send power command to the TV
offline.communication-error-set-volume = Unable to set volume on the TV
offline.communication-error-set-mute = Unable to send mute command to the TV
offline.communication-error-set-source = Unable to set current source on the TV
offline.communication-error-launch-app = Unable to launch app on the TV
offline.communication-error-send-cmd = Unable to send control command to the TV
offline.communication-error-send-key = Unable to send keypress to the TV

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="vizio"
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">
<!-- Vizio TV Thing -->
<thing-type id="vizio_tv">
<label>Vizio TV</label>
<description>
A Vizio SmartCast TV
</description>
<channels>
<channel id="power" typeId="system.power"/>
<channel id="volume" typeId="system.volume"/>
<channel id="mute" typeId="system.mute"/>
<channel id="source" typeId="source"/>
<channel id="activeApp" typeId="activeApp"/>
<channel id="control" typeId="control"/>
<channel id="button" typeId="buttonTv"/>
</channels>
<properties>
<property name="modelId">unknown</property>
</properties>
<representation-property>uuid</representation-property>
<config-description>
<parameter name="hostName" type="text" required="true">
<context>network-address</context>
<label>Host Name/IP Address</label>
<description>Host Name or IP Address of the Vizio TV</description>
</parameter>
<parameter name="port" type="integer" min="1" max="65535" required="true">
<label>Port</label>
<description>Port for the Vizio TV</description>
<default>7345</default>
<limitToOptions>true</limitToOptions>
<options>
<option value="7345">7345 (Newer Models)</option>
<option value="9000">9000 (Older Models)</option>
</options>
</parameter>
<parameter name="authToken" type="text" required="false">
<label>Auth Token</label>
<description>Auth Token that is obtained via the pairing process; See documentation for details</description>
</parameter>
<parameter name="appListJson" type="text" required="false">
<context>script</context>
<label>App List Configuration</label>
<description>The JSON configuration string for the list of apps available in the activeApp channel drop down</description>
</parameter>
</config-description>
</thing-type>
<channel-type id="source">
<item-type>String</item-type>
<label>Source Input</label>
<description>Select the Source Input for the TV</description>
</channel-type>
<channel-type id="activeApp">
<item-type>String</item-type>
<label>Active App</label>
<description>The currently running App on the TV</description>
</channel-type>
<channel-type id="control">
<item-type>Player</item-type>
<label>Control</label>
<description>Transport Controls e.g. Play/Pause/Next/Previous/FForward/Rewind</description>
<category>Player</category>
</channel-type>
<channel-type id="buttonTv">
<item-type>String</item-type>
<label>Remote Button</label>
<description>A Remote Button press to send to the TV</description>
<state>
<options>
<option value="PowerOn">Power On</option>
<option value="PowerOff">Power Off</option>
<option value="PowerToggle">Power Toggle</option>
<option value="VolumeUp">Volume Up</option>
<option value="VolumeDown">Volume Down</option>
<option value="MuteOn">Mute On</option>
<option value="MuteOff">Mute Off</option>
<option value="MuteToggle">Mute Toggle</option>
<option value="ChannelUp">Channel Up</option>
<option value="ChannelDown">Channel Down</option>
<option value="PreviousCh">Previous Channel</option>
<option value="InputToggle">Input Toggle</option>
<option value="SeekFwd">Seek Fwd</option>
<option value="SeekBack">Seek Back</option>
<option value="Play">Play</option>
<option value="Pause">Pause</option>
<option value="Up">Up</option>
<option value="Down">Down</option>
<option value="Left">Left</option>
<option value="Right">Right</option>
<option value="Ok">Ok</option>
<option value="Back">Back</option>
<option value="Info">Info</option>
<option value="Menu">Menu</option>
<option value="Home">Home</option>
<option value="Exit">Exit</option>
<option value="Smartcast">Smartcast</option>
<option value="ccToggle">CC Toggle</option>
<option value="PictureMode">Picture Mode</option>
<option value="WideMode">Wide Mode</option>
<option value="WideToggle">Wide Toggle</option>
</options>
</state>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,228 @@
{
"Apps": [
{
"name": "SmartCast Home",
"config": {
"APP_ID": "1",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "Apple TV+",
"config": {
"APP_ID": "4",
"NAME_SPACE": 3,
"MESSAGE": null
}
},
{
"name": "CBS News",
"config": {
"APP_ID": "42",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "Crackle",
"config": {
"APP_ID": "5",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "discovery+",
"config": {
"APP_ID": "130",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "Disney+",
"config": {
"APP_ID": "75",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "FilmRise",
"config": {
"APP_ID": "24",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "Haystack News",
"config": {
"APP_ID": "60",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "HBO Max",
"config": {
"APP_ID": "128",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "Hulu",
"config": {
"APP_ID": "3",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "iHeartRadio",
"config": {
"APP_ID": "6",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "Movies Anywhere",
"config": {
"APP_ID": "38",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "NBC",
"config": {
"APP_ID": "10",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "Netflix",
"config": {
"APP_ID": "1",
"NAME_SPACE": 3,
"MESSAGE": null
}
},
{
"name": "Newsy",
"config": {
"APP_ID": "15",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "Paramount+",
"config": {
"APP_ID": "37",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "Peacock",
"config": {
"APP_ID": "88",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "Plex",
"config": {
"APP_ID": "9",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "Pluto TV",
"config": {
"APP_ID": "E6F74C01",
"NAME_SPACE": 0,
"MESSAGE": "{\"CAST_NAMESPACE\": \"urn:x-cast:tv.pluto\",\"CAST_MESSAGE\": {\"command\": \"initializePlayback\",\"channel\": \"\",\"episode\": \"\",\"time\": 0}}"
}
},
{
"name": "Prime Video",
"config": {
"APP_ID": "3",
"NAME_SPACE": 3,
"MESSAGE": null
}
},
{
"name": "Redbox",
"config": {
"APP_ID": "41",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "Starz",
"config": {
"APP_ID": "151",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "Vudu",
"config": {
"APP_ID": "31",
"NAME_SPACE": 4,
"MESSAGE": "https://my.vudu.com/castReceiver/index.html?launch-source=app-icon"
}
},
{
"name": "XUMO",
"config": {
"APP_ID": "62",
"NAME_SPACE": 4,
"MESSAGE": "{\"CAST_NAMESPACE\": \"urn:x-cast:com.google.cast.media\",\"CAST_MESSAGE\": {\"type\": \"LOAD\",\"media\": {},\"autoplay\": true,\"currentTime\": 0,\"customData\": {}}}"
}
},
{
"name": "YouTube",
"config": {
"APP_ID": "1",
"NAME_SPACE": 5,
"MESSAGE": null
}
},
{
"name": "YouTubeTV",
"config": {
"APP_ID": "3",
"NAME_SPACE": 5,
"MESSAGE": null
}
},
{
"name": "WatchFree Plus",
"config": {
"APP_ID": "3014",
"NAME_SPACE": 4,
"MESSAGE": null
}
},
{
"name": "WatchFree+ AVOD",
"config": {
"APP_ID": "145",
"NAME_SPACE": 4,
"MESSAGE": null
}
}
]
}

View File

@ -390,6 +390,7 @@
<module>org.openhab.binding.vesync</module> <module>org.openhab.binding.vesync</module>
<module>org.openhab.binding.vigicrues</module> <module>org.openhab.binding.vigicrues</module>
<module>org.openhab.binding.vitotronic</module> <module>org.openhab.binding.vitotronic</module>
<module>org.openhab.binding.vizio</module>
<module>org.openhab.binding.volvooncall</module> <module>org.openhab.binding.volvooncall</module>
<module>org.openhab.binding.warmup</module> <module>org.openhab.binding.warmup</module>
<module>org.openhab.binding.weathercompany</module> <module>org.openhab.binding.weathercompany</module>