/* * UnifiConnectChild-UC-Display-27 * * Description: * This Hubitat driver provides a spot to put data from Ubiquiti Connect Display 27 devices. It does not belong on it's own and requires * the UnifiConnectAPI driver as a parent device. * * Instructions for using Tile Template method (originally based on @mircolino's HTML Templates): * 1) In "Hubitat -> Devices" select the child/sensor (not the parent) you would like to "templetize" * 2) In "Preferences -> Tile Template" enter your template (example below) and click "Save Preferences" * Ex: "[font size='2'][b]Temperature:[/b] ${ temperature }°${ location.getTemperatureScale() }[/br][/font]" * 3) In a Hubitat dashboard, add a new tile, and select the child/sensor, in the center select "Attribute", and on the right select the "Tile" attribute * 4) Select the Add Tile button and the tile should appear * NOTE: Should accept most HTML formatting commands with [] instead of <> * * Features List: * Ability to turn on/off the display * Ability to set the volume of the display * Ability to set the brightness of the display * Ability to start/stop locating light * Ability to reboot the display * Ability to check a website (mine) to notify user if there is a newer version of the driver available * * Licensing: * Copyright 2024 David Snell * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * for the specific language governing permissions and limitations under the License. * * Version Control: * 0.1.1 - Renaming of driver-specific attributes and correction to ProcessEvent * 0.1.0 - Initial version * * Thank you(s): * @Cobra for inspiration of how I perform driver version checking * @mircolino for HTML Template method for dashboard use */ // Returns the driver name def DriverName(){ return "UnifiConnectChild-UC-Display-27" } // Returns the driver version def DriverVersion(){ return "0.1.1" } // Driver Metadata metadata{ definition( name: "UnifiConnectChild-UC-Display-27", namespace: "Snell", author: "David Snell", importUrl: "https://www.drdsnell.com/projects/hubitat/drivers/UnifiConnectChild-UC-Display-27.groovy" ) { capability "Sensor" capability "Actuator" capability "Switch" // Commands //command "DoSomething" // For testing and development purposes only, it should not be uncommented for normal use command "play" // command "stop" // //command "display_switch" // command "start_locating" // command "stop_locating" // command "reboot" // command "set_brightness", [ [ name: "Brightness", type: "NUMBER", range: "0..200" ] ] // command "enable_auto_rotate" // command "set_volume", [ [ name: "Volume", type: "NUMBER", range: "0..40" ] ] // //command "fw_update" // command "enable_sleep" // command "disable_sleep" // //command "enable_memorize_playlist" // //command "disable_memorize_playlist" // command "disable_auto_rotate" // command "rotate", [ [ name: "Rotation", type: "ENUM", constraints: [ "Portrait", "Landscape", "Portrait (flipped)", "Landscape (flipped)" ] ] ] // //command "load_website" // //command "refresh_website" // command "enable_auto_reload" // command "disable_auto_reload" // //command "upgrade_mode" // //command "launch_app" // //command "stop_app" // // Attributes for the driver itself attribute "DriverName", "string" // Identifies the driver being used for update purposes attribute "DriverVersion", "string" // Handles version for driver attribute "DriverStatus", "string" // Handles version notices for driver // Attributes - Device Related attribute "Status", "string" // Show success/failure of commands performed attribute "Device Status", "string" // Show the current state of the device as reported by the controller attribute "Device Type", "string" // What type of device the child is respresenting // General Device Attributes attribute "Type", "string" // The type of device, per API attribute "ID", "string" // ID of the device, per API attribute "Mode", "string" // attribute "Volume", "number" // Max volume is 40? attribute "Brightness", "number" // Max brightness is 255 attribute "switch", "enum", [ "on", "off" ] attribute "Rotation", "enum", [ "Portrait", "Landscape", "Portrait (flipped)", "Landscape (flipped)" ] // // Tile Template attribute attribute "Tile", "string"; // Ex: "[font size='2'][b]Temperature:[/b] ${ temperature }°${ location.getTemperatureScale() }[/br][/font]" } preferences{ //section{ if( ShowAllPreferences ){ //input( type: "string", name: "DeviceName", title: "Device Name", description: "If set it will change the device's name on the controller.", defaultValue: "${ device.label }") input( name: "TileTemplate", type: "string", title: "Tile Template", description: "Ex: [b]Temperature:[/b] \${ state.temperature }°${ location.getTemperatureScale() }[/br]", defaultValue: ""); input( type: "enum", name: "LogType", title: "Enable Logging?", required: false, multiple: false, options: [ "None", "Info", "Debug", "Trace" ], defaultValue: "Info" ) input( type: "bool", name: "ShowAllPreferences", title: "Show All Preferences?", defaultValue: true ) } else { input( type: "bool", name: "ShowAllPreferences", title: "Show All Preferences?", defaultValue: true ) } //} } } // updated def updated(){ if( LogType == null ){ LogType = "Info" } if( state."Driver Status" != null ){ state.remove( "Driver Name" ) state.remove( "Driver Version" ) state.remove( "Driver Status" ) device.deleteCurrentState( "Driver Status" ) device.deleteCurrentState( "Driver Name" ) device.deleteCurrentState( "Driver Version" ) } ProcessEvent( "DriverName", "${ DriverName() }" ) ProcessEvent( "DriverVersion", "${ DriverVersion() }" ) ProcessEvent( "DriverStatus", null ) // Schedule daily check for driver updates to notify user def Hour = ( new Date().format( "h" ) as int ) def Minute = ( new Date().format( "m" ) as int ) def Second = ( new Date().format( "s" ) as int ) // Schedule checks that are only performed once a day schedule( "${ Second } ${ Minute } ${ Hour } ? * *", "CheckForUpdate" ) Logging( "Updated", 2 ) // Check for device settings that may need to be set /* if( DeviceName != null && DeviceName != device.label ){ if( state.ID != null ){ parent.SetChildName( device.getDeviceNetworkId(), state.ID, "{\"name\":\"${ DeviceName }\"}" ) } } else { if( state.ID != null ){ parent.SetChildName( device.getDeviceNetworkId(), state.ID, "{\"name\":\"${ device.label }\"}" ) } } */ } // DoSomething is for testing and development purposes. It should not be uncommented for normal usage. def DoSomething(){ } // Send a command to turn on the display def on(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "56938f33-a5d6-41fc-9817-6a948dc7bf68", "display_on" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot turn on", 5 ) } } // Send a command to turn off the display def off(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "ea666ee3-b36f-48c2-8c38-cac57e2710b3", "display_off" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot turn off", 5 ) } } // Send a command to set the display to start playing def play(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "4dfac488-8230-4570-add6-8fa619ac9977", "play" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot play", 5 ) } } // Send a command to set the display to stop def stop(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "83729c48-c7b4-4f24-b610-8a01858ee56f", "stop" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot stop", 5 ) } } // Send a command to set the display to switch def display_switch(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "dd0ca6ce-9b9f-4f83-bd30-40dbfee33dba", "switch" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot switch", 5 ) } } // Send a command to set the display to start locating def start_locating(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "cb2bb4c3-d121-432b-bf1f-f42859722310", "start_locating" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot start locating", 5 ) } } // Send a command to set the display to stop locating def stop_locating(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "07be223c-450f-44e4-b1ff-f8ea3d49b4d6", "stop_locating" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot stop locating", 5 ) } } // Send a command to set the display to reboot def reboot(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "e663ef02-e88a-46d2-b406-f0fd53d14a50", "reboot" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot reboot", 5 ) } } // Send a command to set the display to set brightness def set_brightness( Value ){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "4d76117c-9c7e-4d9c-8bf0-cb4ad623ba26", "brightness", "\"value\":${ Value }" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot set brightness", 5 ) } } // Send a command to set the display to enable auto rotate def enable_auto_rotate(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "bb211c47-89a7-4c54-a5fa-ddfb6235321e", "enable_auto_rotate" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot set enable auto rotate", 5 ) } } // Send a command to set the display to set volume def set_volume( Value ){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "d05b619c-dfb6-495b-af8b-85051d86d7a2", "volume", "\"value\":${ Value }" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot set volume", 5 ) } } // Send a command to set the display to firmware update def fw_update(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "9958b159-33b3-48ad-922c-be2923076d46", "fw update" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot firmware update", 5 ) } } // Send a command to set the display to enable sleep def enable_sleep(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "6192dbac-869e-47ee-9e3d-76768dfe78d3", "enable_sleep" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot enable sleep", 5 ) } } // Send a command to set the display to disable sleep def disable_sleep(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "3109519b-fdbf-4be1-a094-78e1de039d66", "disable_sleep" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot disable sleep", 5 ) } } // Send a command to set the display to enable memorize playlist def enable_memorize_playlist(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "dfcf2c1b-162e-402a-9b41-8a84fa351a91", "enable_memorize_playlist" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot enable memorize playlist", 5 ) } } // Send a command to set the display to disable memorize playlist def disable_memorize_playlist(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "88da3e40-d6e7-4de2-ae10-a065fd65567f", "disable_memorize_playlist" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot disable memorize playlist", 5 ) } } // Send a command to set the display to disable auto rotate def disable_auto_rotate(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "468975da-b592-4df0-87b9-c76f01a30dcf", "disable_auto_rotate" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot disable auto rotate", 5 ) } } // Send a command to set the display to rotate def rotate( Value ){ if( state.ID != null ){ def Args switch( Value ){ case "Portrait": Args = "\"scale\":\"portraitPrim\"" break case "Landscape": Args = "\"scale\":\"landscapePrim\"" break case "Portrait (flipped)": Args = "\"scale\":\"portraitSec\"" break case "Landscape (flipped)": Args = "\"scale\":\"landscapeSec\"" break } parent.PerformAction( device.getDeviceNetworkId(), state.ID, "66e7ddaa-afd8-4fe7-9d3c-46974b0936a7", "rotate", Args ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot rotate", 5 ) } } // Send a command to set the display to load website def load_website(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "029168de-0d42-43a9-becd-9e0053c2cb84", "load_website" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot load website", 5 ) } } // Send a command to set the display to refresh website def refresh_website(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "6f9ff80f-c85e-4492-b751-be4c198d3634", "refresh_website" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot refresh website", 5 ) } } // Send a command to set the display to enable auto reload def enable_auto_reload(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "dd57f016-6e3a-47af-b74a-83bdec9ad02c", "enable_auto_reload" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot enable auto reload", 5 ) } } // Send a command to set the display to disable auto reload def disable_auto_reload(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "63fab99a-fb21-4b9b-91aa-d7ce676bd116", "disable_auto_reload" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot disable auto reload", 5 ) } } // Send a command to set the display to upgrade mode def upgrade_mode(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "41e79ad0-4fd8-4aaa-9723-a3d48da650f5", "upgrade_mode" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot upgrade mode", 5 ) } } // Send a command to set the display to launch app def launch_app(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "4279f53f-8f72-4e3b-a23a-d805c461e7aa", "launch_app" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot launch app", 5 ) } } // Send a command to set the display to stop app def stop_app(){ if( state.ID != null ){ parent.PerformAction( device.getDeviceNetworkId(), state.ID, "58bbb1d2-f26b-47c7-89dc-b0961a2e9d5e", "stop_app" ) } else { Logging( "No ID for ${ device.getDeviceNetworkId() }, cannot stop app", 5 ) } } // installed is called when the device is installed, all it really does is run updated def installed(){ Logging( "Installed", 2 ) updated() } // initialize is called when the device is initialized, all it really does is run updated def initialize(){ Logging( "Initialized", 2 ) updated() } // Return a state value def ReturnState( Variable ){ return state."${ Variable }" } // Tile Template method based on @mircolino's HTML Template method private void UpdateTile( String val ){ if( settings.TileTemplate ){ // Create special compound/html tile val = settings.TileTemplate.toString().replaceAll( "\\[", "<" ) val = val.replaceAll( "\\]", ">" ) val = val.replaceAll( ~/\$\{\s*([A-Za-z][A-Za-z0-9_]*)\s*\}/ ) { java.util.ArrayList m -> device.currentValue("${ m [ 1 ] }").toString() } if( device.currentValue( "Tile" ).toString() != val ){ sendEvent( name: "Tile", value: val ) } } } // Process data to check against current state value and then send an event if it has changed def ProcessEvent( Variable, Value, Unit = null, ForceEvent = false ){ if( ( state."${ Variable }" != Value ) || ( ForceEvent == true ) ){ state."${ Variable }" = Value if( Unit != null ){ Logging( "Event: ${ Variable } = ${ Value }${ Unit }", 4 ) sendEvent( name: "${ Variable }", value: Value, unit: Unit, isStateChange: true ) } else { Logging( "Event: ${ Variable } = ${ Value }", 4 ) sendEvent( name: "${ Variable }", value: Value, isStateChange: true ) } UpdateTile( "${ Value }" ) } } // Process data to check against current state value def ProcessState( Variable, Value ){ if( state."${ Variable }" != Value ){ Logging( "State: ${ Variable } = ${ Value }", 4 ) state."${ Variable }" = Value UpdateTile( "${ Value }" ) } } // Handles whether logging is enabled and thus what to put there. def Logging( LogMessage, LogLevel ){ // Add all messages as info logging if( ( LogLevel == 2 ) && ( LogType != "None" ) ){ log.info( "${ device.displayName } - ${ LogMessage }" ) } else if( ( LogLevel == 3 ) && ( ( LogType == "Debug" ) || ( LogType == "Trace" ) ) ){ log.debug( "${ device.displayName } - ${ LogMessage }" ) } else if( ( LogLevel == 4 ) && ( LogType == "Trace" ) ){ log.trace( "${ device.displayName } - ${ LogMessage }" ) } else if( LogLevel == 5 ){ log.error( "${ device.displayName } - ${ LogMessage }" ) } } // Checks drdsnell.com for the latest version of the driver // Original inspiration from @cobra's version checking def CheckForUpdate(){ ProcessEvent( "DriverName", DriverName(), null, true ) ProcessEvent( "DriverVersion", DriverVersion(), null, true ) ProcessEvent( "DriverStatus", null, null, true ) httpGet( uri: "https://www.drdsnell.com/projects/hubitat/drivers/versions.json", contentType: "application/json" ){ resp -> switch( resp.status ){ case 200: if( resp.data."${ DriverName() }" ){ CurrentVersion = DriverVersion().split( /\./ ) if( resp.data."${ DriverName() }".version == "REPLACED" ){ ProcessEvent( "DriverStatus", "Driver replaced, please use ${ resp.data."${ state.DriverName }".file }", null, true ) } else if( resp.data."${ DriverName() }".version == "REMOVED" ){ ProcessEvent( "DriverStatus", "Driver removed and no longer supported.", null, true ) } else { SiteVersion = resp.data."${ DriverName() }".version.split( /\./ ) if( CurrentVersion == SiteVersion ){ Logging( "Driver version up to date", 3 ) ProcessEvent( "DriverStatus", "Up to date" ) } else if( ( CurrentVersion[ 0 ] as int ) > ( SiteVersion [ 0 ] as int ) ){ Logging( "Major development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version", 3 ) ProcessEvent( "DriverStatus", "Major development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version", null, true ) } else if( ( CurrentVersion[ 1 ] as int ) > ( SiteVersion [ 1 ] as int ) ){ Logging( "Minor development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version", 3 ) ProcessEvent( "DriverStatus", "Minor development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version", null, true ) } else if( ( CurrentVersion[ 2 ] as int ) > ( SiteVersion [ 2 ] as int ) ){ Logging( "Patch development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version", 3 ) ProcessEvent( "DriverStatus", "Patch development ${ CurrentVersion[ 0 ] }.${ CurrentVersion[ 1 ] }.${ CurrentVersion[ 2 ] } version", null, true ) } else if( ( SiteVersion[ 0 ] as int ) > ( CurrentVersion[ 0 ] as int ) ){ Logging( "New major release ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available", 2 ) ProcessEvent( "DriverStatus", "New major release ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available", null, true ) } else if( ( SiteVersion[ 1 ] as int ) > ( CurrentVersion[ 1 ] as int ) ){ Logging( "New minor release ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available", 2 ) ProcessEvent( "DriverStatus", "New minor release ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available", null, true ) } else if( ( SiteVersion[ 2 ] as int ) > ( CurrentVersion[ 2 ] as int ) ){ Logging( "New patch ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available", 2 ) ProcessEvent( "DriverStatus", "New patch ${ SiteVersion[ 0 ] }.${ SiteVersion[ 1 ] }.${ SiteVersion[ 2 ] } available", null, true ) } } } else { Logging( "${ DriverName() } is not published on drdsnell.com", 2 ) ProcessEvent( "DriverStatus", "${ DriverName() } is not published on drdsnell.com", null, true ) } break default: Logging( "Unable to check drdsnell.com for ${ DriverName() } driver updates.", 2 ) break } } }