/* groovylint-disable CompileStatic, CouldBeSwitchStatement, DuplicateListLiteral, DuplicateNumberLiteral, DuplicateStringLiteral, ImplicitClosureParameter, ImplicitReturnStatement, Instanceof, LineLength, MethodCount, MethodSize, NoDouble, NoFloat, NoWildcardImports, ParameterName, UnnecessaryElseStatement, UnnecessaryGetter, UnnecessaryPublicModifier, UnnecessarySetter, UnusedImport */
library(
base: 'driver',
author: 'Krassimir Kossev',
category: 'zigbee',
description: 'Device Profile Library',
name: 'deviceProfileLib',
namespace: 'kkossev',
importUrl: 'https://raw.githubusercontent.com/kkossev/hubitat/development/libraries/deviceProfileLib.groovy',
version: '3.1.2',
documentationLink: ''
)
/*
* Device Profile Library
*
* Licensed Virtual 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.
*
* ver. 1.0.0 2023-11-04 kkossev - added deviceProfileLib (based on Tuya 4 In 1 driver)
* ver. 3.0.0 2023-11-27 kkossev - (dev. branch) fixes for use with commonLib; added processClusterAttributeFromDeviceProfile() method; added validateAndFixPreferences() method; inputIt bug fix; signedInt Preproc method;
* ver. 3.0.1 2023-12-02 kkossev - (dev. branch) release candidate
* ver. 3.0.2 2023-12-17 kkossev - (dev. branch) inputIt moved to the preferences section; setfunction replaced by customSetFunction; Groovy Linting;
* ver. 3.0.4 2024-03-30 kkossev - (dev. branch) more Groovy Linting; processClusterAttributeFromDeviceProfile exception fix;
* ver. 3.1.0 2024-04-03 kkossev - (dev. branch) more Groovy Linting; deviceProfilesV3, enum pars bug fix;
* ver. 3.1.1 2024-04-21 kkossev - (dev. branch) deviceProfilesV3 bug fix; tuyaDPs list of maps bug fix; resetPreferencesToDefaults bug fix;
* ver. 3.1.2 2024-05-05 kkossev - (dev. branch) added isSpammyDeviceProfile()
*
* TODO - updateStateUnknownDPs !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* TODO - send info log only if the value has changed? // TODO - check whether Info log will be sent also for spammy clusterAttribute ?
* TODO: refactor sendAttribute ! sendAttribute exception bug fix for virtual devices; check if String getObjectClassName(Object o) is in 2.3.3.137, can be used?
* TODO: handle preferences of a type TEXT
*
*/
static String deviceProfileLibVersion() { '3.1.2' }
static String deviceProfileLibStamp() { '2024/05/05 9:02 PM' }
import groovy.json.*
import groovy.transform.Field
import hubitat.zigbee.clusters.iaszone.ZoneStatus
import hubitat.zigbee.zcl.DataType
import java.util.concurrent.ConcurrentHashMap
import groovy.transform.CompileStatic
metadata {
// no capabilities
// no attributes
command 'sendCommand', [
[name:'command', type: 'STRING', description: 'command name', constraints: ['STRING']],
[name:'val', type: 'STRING', description: 'command parameter value', constraints: ['STRING']]
]
command 'setPar', [
[name:'par', type: 'STRING', description: 'preference parameter name', constraints: ['STRING']],
[name:'val', type: 'STRING', description: 'preference parameter value', constraints: ['STRING']]
]
preferences {
// itterate over DEVICE.preferences map and inputIt all
if (DEVICE != null && DEVICE?.preferences != null && DEVICE?.preferences != [:]) {
(DEVICE?.preferences).each { key, value ->
if (inputIt(key) != null) {
input inputIt(key)
}
}
if (('motionReset' in DEVICE?.preferences) && (DEVICE?.preferences.motionReset == true)) {
input(name: 'motionReset', type: 'bool', title: 'Reset Motion to Inactive', description: 'Software Reset Motion to Inactive after timeout. Recommended value is false', defaultValue: false)
if (motionReset.value == true) {
input('motionResetTimer', 'number', title: 'Motion Reset Timer', description: 'After motion is detected, wait ___ second(s) until resetting to inactive state. Default = 60 seconds', range: '0..7200', defaultValue: 60)
}
}
}
if (advancedOptions == true) {
input(name: 'forcedProfile', type: 'enum', title: 'Device Profile', description: 'Forcely change the Device Profile, if the model/manufacturer was not recognized automatically.
Warning! Manually setting a device profile may not always work!', options: getDeviceProfilesMap())
}
}
}
boolean is2in1() { return getDeviceProfile().contains('TS0601_2IN1') }
String getDeviceProfile() { state.deviceProfile ?: 'UNKNOWN' }
Map getDEVICE() { deviceProfilesV3 != null ? deviceProfilesV3[getDeviceProfile()] : deviceProfilesV2[getDeviceProfile()] }
Set getDeviceProfiles() { deviceProfilesV3 != null ? deviceProfilesV3?.keySet() : deviceProfilesV2?.keySet() }
List getDeviceProfilesMap() { deviceProfilesV3 != null ? deviceProfilesV3.values().description as List : deviceProfilesV2.values().description as List }
// ---------------------------------- deviceProfilesV3 helper functions --------------------------------------------
/**
* Returns the profile key for a given profile description.
* @param valueStr The profile description to search for.
* @return The profile key if found, otherwise null.
*/
String getProfileKey(final String valueStr) {
if (deviceProfilesV3 != null) { return deviceProfilesV3.find { _, profileMap -> profileMap.description == valueStr }?.key }
else if (deviceProfilesV2 != null) { return deviceProfilesV2.find { _, profileMap -> profileMap.description == valueStr }?.key }
else { return null }
}
/**
* Finds the preferences map for the given parameter.
* @param param The parameter to find the preferences map for.
* @param debug Whether or not to output debug logs.
* @return returns either tuyaDPs or attributes map, depending on where the preference (param) is found
* @return empty map [:] if param is not defined for this device.
*/
Map getPreferencesMapByName(final String param, boolean debug=false) {
Map foundMap = [:]
if (!(param in DEVICE?.preferences)) {
if (debug) { log.warn "getPreferencesMapByName: preference ${param} not defined for this device!" }
return [:]
}
/* groovylint-disable-next-line NoDef, VariableTypeRequired */
def preference
try {
preference = DEVICE?.preferences["$param"]
if (debug) { log.debug "getPreferencesMapByName: preference ${param} found. value is ${preference}" }
if (preference in [true, false]) {
// find the preference in the tuyaDPs map
logDebug "getPreferencesMapByName: preference ${param} is boolean"
return [:] // no maps for predefined preferences !
}
if (safeToInt(preference, -1) > 0) { //if (preference instanceof Number) {
int dp = safeToInt(preference)
//if (debug) log.trace "getPreferencesMapByName: param ${param} preference ${preference} is number (${dp})"
foundMap = DEVICE?.tuyaDPs.find { it.dp == dp }
}
else { // cluster:attribute
//if (debug) { log.trace "${DEVICE?.attributes}" }
foundMap = DEVICE?.attributes.find { it.at == preference }
}
// TODO - could be also 'true' or 'false' ...
} catch (e) {
if (debug) { log.warn "getPreferencesMapByName: exception ${e} caught when getting preference ${param} !" }
return [:]
}
if (debug) { log.debug "getPreferencesMapByName: foundMap = ${foundMap}" }
return foundMap
}
Map getAttributesMap(String attribName, boolean debug=false) {
Map foundMap = [:]
List