diff --git a/CODEOWNERS b/CODEOWNERS
index 2714540d6da..a275a0b71cc 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -358,6 +358,7 @@
/bundles/org.openhab.binding.vesync/ @dag81
/bundles/org.openhab.binding.vigicrues/ @clinique
/bundles/org.openhab.binding.vitotronic/ @steand
+/bundles/org.openhab.binding.vizio/ @mlobstein
/bundles/org.openhab.binding.volvooncall/ @clinique @Jamstah
/bundles/org.openhab.binding.warmup/ @jamesmelville
/bundles/org.openhab.binding.weathercompany/ @mhilbush
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index 53d0bec852d..52b327901c7 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -1781,6 +1781,11 @@
org.openhab.binding.vitotronic
${project.version}
+
+ org.openhab.addons.bundles
+ org.openhab.binding.vizio
+ ${project.version}
+
org.openhab.addons.bundles
org.openhab.binding.volvooncall
diff --git a/bundles/org.openhab.binding.vizio/NOTICE b/bundles/org.openhab.binding.vizio/NOTICE
new file mode 100644
index 00000000000..38d625e3492
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/NOTICE
@@ -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
diff --git a/bundles/org.openhab.binding.vizio/README.md b/bundles/org.openhab.binding.vizio/README.md
new file mode 100644
index 00000000000..7c72f7b0ffa
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/README.md
@@ -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 start_pairing
+```
+
+Substitute `` with thing's id, ie: `vizio_tv:00bc3e711660`
+Substitute `` the desired device name that will appear in the TV's settings, under Mobile Devices, ie: `Vizio-openHAB`
+
+Submit Pairing Code:
+
+```
+openhab:vizio submit_code
+```
+
+Substitute `` with the same thing id used above
+Substitute `` 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` & `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
+ }
+}
+
+```
diff --git a/bundles/org.openhab.binding.vizio/pom.xml b/bundles/org.openhab.binding.vizio/pom.xml
new file mode 100644
index 00000000000..34c5fbd89a6
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/pom.xml
@@ -0,0 +1,17 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 3.4.0-SNAPSHOT
+
+
+ org.openhab.binding.vizio
+
+ openHAB Add-ons :: Bundles :: Vizio Binding
+
+
diff --git a/bundles/org.openhab.binding.vizio/src/main/feature/feature.xml b/bundles/org.openhab.binding.vizio/src/main/feature/feature.xml
new file mode 100644
index 00000000000..83ec41e2489
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/feature/feature.xml
@@ -0,0 +1,10 @@
+
+
+ mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features
+
+
+ openhab-runtime-base
+ openhab-transport-mdns
+ mvn:org.openhab.addons.bundles/org.openhab.binding.vizio/${project.version}
+
+
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioBindingConstants.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioBindingConstants.java
new file mode 100644
index 00000000000..3198cd7c1e6
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioBindingConstants.java
@@ -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 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";
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioConfiguration.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioConfiguration.java
new file mode 100644
index 00000000000..ab95e298cda
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioConfiguration.java
@@ -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;
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioException.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioException.java
new file mode 100644
index 00000000000..21ccb4df918
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioException.java
@@ -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);
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioHandlerFactory.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioHandlerFactory.java
new file mode 100644
index 00000000000..9c7be002e3f
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioHandlerFactory.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioStateDescriptionOptionProvider.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioStateDescriptionOptionProvider.java
new file mode 100644
index 00000000000..5a51cbccf2c
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/VizioStateDescriptionOptionProvider.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/appdb/VizioAppDbService.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/appdb/VizioAppDbService.java
new file mode 100644
index 00000000000..a1694d0216b
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/appdb/VizioAppDbService.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioCommunicator.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioCommunicator.java
new file mode 100644
index 00000000000..d7095938c8b
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioCommunicator.java
@@ -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 fromJson(String json, Class 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);
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioTlsTrustManagerProvider.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioTlsTrustManagerProvider.java
new file mode 100644
index 00000000000..67f35616e01
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/communication/VizioTlsTrustManagerProvider.java
@@ -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();
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/console/VizioCommandExtension.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/console/VizioCommandExtension.java
new file mode 100644
index 00000000000..e2ae8609533
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/console/VizioCommandExtension.java
@@ -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 + " ");
+ } 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
+ + " ");
+ 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 getUsages() {
+ return List.of(new String[] {
+ buildCommandUsage(" " + START_PAIRING + " ", "start pairing process"),
+ buildCommandUsage(" " + SUBMIT_CODE + " ", "submit pairing code") });
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/discovery/VizioDiscoveryParticipant.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/discovery/VizioDiscoveryParticipant.java
new file mode 100644
index 00000000000..f45f8d4bb4f
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/discovery/VizioDiscoveryParticipant.java
@@ -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 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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/Item.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/Item.java
new file mode 100644
index 00000000000..f7bb76fafe2
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/Item.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/Parameters.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/Parameters.java
new file mode 100644
index 00000000000..dfcdcc6b2b0
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/Parameters.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/PutResponse.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/PutResponse.java
new file mode 100644
index 00000000000..76b6b746b77
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/PutResponse.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/Status.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/Status.java
new file mode 100644
index 00000000000..b0befb8cbb0
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/Status.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/Value.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/Value.java
new file mode 100644
index 00000000000..1e65781c875
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/Value.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/app/CurrentApp.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/app/CurrentApp.java
new file mode 100644
index 00000000000..81b5c9ccce5
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/app/CurrentApp.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/app/ItemApp.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/app/ItemApp.java
new file mode 100644
index 00000000000..2728a0f722e
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/app/ItemApp.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/app/ItemAppValue.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/app/ItemAppValue.java
new file mode 100644
index 00000000000..d5fdda94f4c
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/app/ItemAppValue.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/applist/VizioApp.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/applist/VizioApp.java
new file mode 100644
index 00000000000..04a77113475
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/applist/VizioApp.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/applist/VizioAppConfig.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/applist/VizioAppConfig.java
new file mode 100644
index 00000000000..b936219d34c
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/applist/VizioAppConfig.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/applist/VizioApps.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/applist/VizioApps.java
new file mode 100644
index 00000000000..8ed45ebed28
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/applist/VizioApps.java
@@ -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 apps = new ArrayList();
+
+ public List getApps() {
+ return apps;
+ }
+
+ public void setApps(List apps) {
+ this.apps = apps;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/audio/Audio.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/audio/Audio.java
new file mode 100644
index 00000000000..d42ee1656ba
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/audio/Audio.java
@@ -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 hashlist = new ArrayList();
+ @SerializedName("GROUP")
+ private String group;
+ @SerializedName("NAME")
+ private String name;
+ @SerializedName("PARAMETERS")
+ private Parameters parameters;
+ @SerializedName("ITEMS")
+ private List items = new ArrayList();
+ @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 getHashlist() {
+ return hashlist;
+ }
+
+ public void setHashlist(List 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 getItems() {
+ return items;
+ }
+
+ public void setItems(List 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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/audio/ItemAudio.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/audio/ItemAudio.java
new file mode 100644
index 00000000000..cc0d094d575
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/audio/ItemAudio.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/input/CurrentInput.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/input/CurrentInput.java
new file mode 100644
index 00000000000..5ca0aa9b6f0
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/input/CurrentInput.java
@@ -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 items = new ArrayList();
+ @SerializedName("HASHLIST")
+ private List hashlist = new ArrayList();
+ @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 getItems() {
+ return items;
+ }
+
+ public void setItems(List items) {
+ this.items = items;
+ }
+
+ public List getHashlist() {
+ return hashlist;
+ }
+
+ public void setHashlist(List 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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/input/ItemInput.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/input/ItemInput.java
new file mode 100644
index 00000000000..d4e0ed4f2ec
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/input/ItemInput.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/inputlist/InputList.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/inputlist/InputList.java
new file mode 100644
index 00000000000..5b2014e18e8
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/inputlist/InputList.java
@@ -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 hashlist = new ArrayList();
+ @SerializedName("GROUP")
+ private String group;
+ @SerializedName("NAME")
+ private String name;
+ @SerializedName("PARAMETERS")
+ private Parameters parameters;
+ @SerializedName("ITEMS")
+ private List- items = new ArrayList
- ();
+ @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 getHashlist() {
+ return hashlist;
+ }
+
+ public void setHashlist(List 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
- getItems() {
+ return items;
+ }
+
+ public void setItems(List
- 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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/pairing/ItemAuthToken.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/pairing/ItemAuthToken.java
new file mode 100644
index 00000000000..1216394db06
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/pairing/ItemAuthToken.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/pairing/ItemPairing.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/pairing/ItemPairing.java
new file mode 100644
index 00000000000..0b198212a57
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/pairing/ItemPairing.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/pairing/PairingComplete.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/pairing/PairingComplete.java
new file mode 100644
index 00000000000..eb1ec052f17
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/pairing/PairingComplete.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/pairing/PairingStart.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/pairing/PairingStart.java
new file mode 100644
index 00000000000..a59fc66c004
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/pairing/PairingStart.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/power/ItemPower.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/power/ItemPower.java
new file mode 100644
index 00000000000..2c5054a6f47
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/power/ItemPower.java
@@ -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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/power/PowerMode.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/power/PowerMode.java
new file mode 100644
index 00000000000..6e5fae41c4c
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/dto/power/PowerMode.java
@@ -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 items = new ArrayList();
+ @SerializedName("URI")
+ private String uri;
+
+ public Status getStatus() {
+ return status;
+ }
+
+ public void setStatus(Status status) {
+ this.status = status;
+ }
+
+ public List getItems() {
+ return items;
+ }
+
+ public void setItems(List items) {
+ this.items = items;
+ }
+
+ public String getUri() {
+ return uri;
+ }
+
+ public void setUri(String uri) {
+ this.uri = uri;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/enums/KeyCommand.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/enums/KeyCommand.java
new file mode 100644
index 00000000000..f52e9cec656
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/enums/KeyCommand.java
@@ -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);
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/handler/VizioHandler.java b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/handler/VizioHandler.java
new file mode 100644
index 00000000000..b3e7131b5e3
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/java/org/openhab/binding/vizio/internal/handler/VizioHandler.java
@@ -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 userConfigApps = new ArrayList();
+ 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 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 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 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 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 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 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 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;
+ }
+}
diff --git a/bundles/org.openhab.binding.vizio/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.vizio/src/main/resources/OH-INF/binding/binding.xml
new file mode 100644
index 00000000000..d0e8c2e6007
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/resources/OH-INF/binding/binding.xml
@@ -0,0 +1,9 @@
+
+
+
+ Vizio Binding
+ Controls Vizio TVs w/SmartCast API (2016+ Models)
+
+
diff --git a/bundles/org.openhab.binding.vizio/src/main/resources/OH-INF/i18n/vizio.properties b/bundles/org.openhab.binding.vizio/src/main/resources/OH-INF/i18n/vizio.properties
new file mode 100644
index 00000000000..c26e2a261f2
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/resources/OH-INF/i18n/vizio.properties
@@ -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
diff --git a/bundles/org.openhab.binding.vizio/src/main/resources/OH-INF/thing/vizio.xml b/bundles/org.openhab.binding.vizio/src/main/resources/OH-INF/thing/vizio.xml
new file mode 100644
index 00000000000..6f7d16fea52
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/resources/OH-INF/thing/vizio.xml
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+ A Vizio SmartCast TV
+
+
+
+
+
+
+
+
+
+
+
+
+
+ unknown
+
+
+ uuid
+
+
+
+ network-address
+
+ Host Name or IP Address of the Vizio TV
+
+
+
+ Port for the Vizio TV
+ 7345
+ true
+
+
+
+
+
+
+
+ Auth Token that is obtained via the pairing process; See documentation for details
+
+
+ script
+
+ The JSON configuration string for the list of apps available in the activeApp channel drop down
+
+
+
+
+
+ String
+
+ Select the Source Input for the TV
+
+
+
+ String
+
+ The currently running App on the TV
+
+
+
+ Player
+
+ Transport Controls e.g. Play/Pause/Next/Previous/FForward/Rewind
+ Player
+
+
+
+ String
+
+ A Remote Button press to send to the TV
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.vizio/src/main/resources/db/apps.json b/bundles/org.openhab.binding.vizio/src/main/resources/db/apps.json
new file mode 100644
index 00000000000..5d3b38db3e3
--- /dev/null
+++ b/bundles/org.openhab.binding.vizio/src/main/resources/db/apps.json
@@ -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
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/bundles/pom.xml b/bundles/pom.xml
index 633af03dcf8..bce1e7d769b 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -390,6 +390,7 @@
org.openhab.binding.vesync
org.openhab.binding.vigicrues
org.openhab.binding.vitotronic
+ org.openhab.binding.vizio
org.openhab.binding.volvooncall
org.openhab.binding.warmup
org.openhab.binding.weathercompany