/**
* Copyright 2020 Markus Liljergren (https://oh-lalabs.com)
*
* Version: v1.1.1.1214Tb
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* NOTE: This is an auto-generated file and most comments have been removed!
*
*/
// BEGIN:getDefaultParentImports()
import groovy.json.JsonSlurper
import groovy.json.JsonOutput
import java.security.MessageDigest
// END: getDefaultParentImports()
metadata {
definition (name: "Tasmota - Universal Parent", namespace: "tasmota", author: "Markus Liljergren", vid: "generic-switch", filename: "tasmota-universal-parent", importUrl: "https://raw.githubusercontent.com/markus-li/Hubitat/development/drivers/expanded/tasmota-universal-parent-expanded.groovy") {
// BEGIN:getDefaultMetadataCapabilities()
capability "Refresh"
capability "Configuration"
// END: getDefaultMetadataCapabilities()
capability "PresenceSensor"
capability "Initialize"
// BEGIN:getDefaultParentMetadataAttributes()
attribute "ip", "string"
attribute "ipLink", "string"
attribute "module", "string"
attribute "templateData", "string"
attribute "wifiSignal", "string"
// END: getDefaultParentMetadataAttributes()
// BEGIN:getDefaultMetadataAttributes()
attribute "driver", "string"
// END: getDefaultMetadataAttributes()
// BEGIN:getMetadataAttributesForLastCheckin()
attribute "lastCheckin", "Date"
attribute "lastCheckinEpoch", "number"
attribute "notPresentCounter", "number"
attribute "restoredCounter", "number"
// END: getMetadataAttributesForLastCheckin()
attribute "commandSent", "string"
attribute "commandResult", "string"
// BEGIN:getMetadataCommandsForHandlingChildDevices()
command "deleteChildren"
// END: getMetadataCommandsForHandlingChildDevices()
// BEGIN:getDefaultMetadataCommands()
command "reboot"
// END: getDefaultMetadataCommands()
// BEGIN:getCommandsForPresence()
command "resetRestoredCounter"
// END: getCommandsForPresence()
command "toggle"
command "sendCommand", [[name:"Command*", type: "STRING", description: "Tasmota Command"],
[name:"Argument", type: "STRING", description: "Argument (optional)"]]
command "parseJSON", [[name:"JSON*", type: "STRING", description: "Tasmota Status as JSON"]]
}
preferences {
// BEGIN:getDefaultParentMetadataPreferences()
input(name: "runReset", description: styling_addDescriptionDiv("For details and guidance, see the Oh-La Labs website. For settings marked as ADVANCED, make sure you understand what they do before activating them. If settings are not reflected on the device, press the Configure button in this driver. Also make sure all settings really are saved and correct."), title: styling_addTitleDiv("Settings"), displayDuringSetup: false, type: "paragraph", element: "paragraph")
input(name: "debugLogging", type: "bool", title: styling_getLogo() + styling_addTitleDiv("Enable debug logging"), description: "" , defaultValue: false, submitOnChange: true, displayDuringSetup: false, required: false)
input(name: "infoLogging", type: "bool", title: styling_addTitleDiv("Enable info logging"), description: "", defaultValue: true, submitOnChange: true, displayDuringSetup: false, required: false)
// END: getDefaultParentMetadataPreferences()
// BEGIN:getMetadataPreferencesForLastCheckin()
input(name: "lastCheckinEnable", type: "bool", title: styling_addTitleDiv("Enable Last Checkin Date"), description: styling_addDescriptionDiv("Records Date events if enabled"), defaultValue: true)
input(name: "lastCheckinEpochEnable", type: "bool", title: styling_addTitleDiv("Enable Last Checkin Epoch"), description: styling_addDescriptionDiv("Records Epoch events if enabled"), defaultValue: false)
input(name: "presenceEnable", type: "bool", title: styling_addTitleDiv("Enable Presence"), description: styling_addDescriptionDiv("Enables Presence to indicate if the device has sent data within the last 3 hours (REQUIRES at least one of the Checkin options to be enabled)"), defaultValue: true)
input(name: "presenceWarningEnable", type: "bool", title: styling_addTitleDiv("Enable Presence Warning"), description: styling_addDescriptionDiv("Enables Presence Warnings in the Logs (default: true)"), defaultValue: true)
// END: getMetadataPreferencesForLastCheckin()
input(name: "deviceConfig", type: "enum", title: styling_addTitleDiv("Device Configuration"),
description: styling_addDescriptionDiv("Select a Device Configuration (default: Generic Device)
'Generic Device' doesn't configure device Template and/or Module on Tasmota. Child devices and types are auto-detected as well as auto-created and does NOT depend on this setting."),
options: getDeviceConfigurationsAsListOption(), defaultValue: "01generic-device",
displayDuringSetup: true, required: false)
// BEGIN:getMetadataPreferencesForHiding()
input(name: "hideExtended", type: "bool", title: styling_addTitleDiv("Hide Extended Settings"), description: styling_addDescriptionDiv("Hides extended settings, usually not needed."), defaultValue: true, displayDuringSetup: false, required: false)
input(name: "hideAdvanced", type: "bool", title: styling_addTitleDiv("Hide Advanced Settings"), description: styling_addDescriptionDiv("Hides advanced settings, usually not needed anyway."), defaultValue: true, displayDuringSetup: false, required: false)
// END: getMetadataPreferencesForHiding()
// BEGIN:getDefaultMetadataPreferencesForTasmota(True) # False = No TelePeriod setting
input("password", "password", title: styling_addTitleDiv("Device Password"), description: styling_addDescriptionDiv("REQUIRED if set on the Device! Otherwise leave empty."))
input(name: "ipAddress", type: "string", title: styling_addTitleDiv("Device IP Address"), description: styling_addDescriptionDiv("Set this as a default fallback for the auto-discovery feature."), displayDuringSetup: true, required: false)
input(name: "port", type: "number", title: styling_addTitleDiv("Device Port"), description: styling_addDescriptionDiv("The http Port of the Device (default: 80)"), displayDuringSetup: true, required: false, defaultValue: 80)
input(name: "override", type: "bool", title: styling_addTitleDiv("Override IP"), description: styling_addDescriptionDiv("Override the automatically discovered IP address and disable auto-discovery."), displayDuringSetup: true, required: false)
input(name: "useIPAsID", type: "bool", title: styling_addTitleDiv("IP as Network ID"), description: styling_addDescriptionDiv("Not needed under normal circumstances. Setting this when not needed can break updates. This requires the IP to be static or set to not change in your DHCP server. It will force the use of IP as network ID. When in use, set Override IP to true and input the correct Device IP Address. See the release thread in the Hubitat forum for details and guidance."), displayDuringSetup: true, required: false)
input(name: "telePeriod", type: "string", title: styling_addTitleDiv("Update Frequency"), description: styling_addDescriptionDiv("Tasmota sensor value update interval, set this to any value between 10 and 3600 seconds. See the Tasmota docs concerning telePeriod for details. This is NOT a poll frequency. Button/switch changes are immediate and are NOT affected by this. This ONLY affects SENSORS and reporting of data such as UPTIME. (default = 300)"), displayDuringSetup: true, required: false)
input(name: "disableModuleSelection", type: "bool", title: styling_addTitleDiv("Disable Automatically Setting Module and Template"), description: "ADVANCED: " + styling_addDescriptionDiv("Disable automatically setting the Module Type and Template in Tasmota. Enable for using custom Module or Template settings directly on the device. With this disabled, you need to set these settings manually on the device."), displayDuringSetup: true, required: false)
input(name: "moduleNumber", type: "number", title: styling_addTitleDiv("Module Number"), description: "ADVANCED: " + styling_addDescriptionDiv("Module Number used in Tasmota. If Device Template is set, this value is IGNORED. (default: -1 (use the default for the driver))"), displayDuringSetup: true, required: false, defaultValue: -1)
input(name: "deviceTemplateInput", type: "string", title: styling_addTitleDiv("Device Template"), description: "ADVANCED: " + styling_addDescriptionDiv("Set this to a Device Template for Tasmota, leave it EMPTY to use the Device Configuration Default. Set it to 0 to NOT use a Template. NAME can be maximum 14 characters! (Example: {\"NAME\":\"S120\",\"GPIO\":[0,0,0,0,0,21,0,0,0,52,90,0,0],\"FLAG\":0,\"BASE\":18})"), displayDuringSetup: true, required: false)
// END: getDefaultMetadataPreferencesForTasmota(True) # False = No TelePeriod setting
input(name: "invertPowerNumber", type: "bool", title: styling_addTitleDiv("Send POWER1 events to POWER2, and vice versa"), description: styling_addDescriptionDiv("Use this if you have a dimmer AND a switch in the same device and on/off is not sent/received correctly. Normally this is NOT needed."), defaultValue: false, displayDuringSetup: false, required: false)
input(name: "useAlternateColorCommand", type: "bool", title: styling_addTitleDiv("Use Alternate Color command in Tasmota"), description: styling_addDescriptionDiv("When enabled, this will use \"Var1\" instead of \"Color1\" in order to be able to catch the command in rules. Normally this is NOT needed."), defaultValue: false, displayDuringSetup: false, required: false)
// BEGIN:getDefaultMetadataPreferencesLast()
input(name: "hideDangerousCommands", type: "bool", title: styling_addTitleDiv("Hide Dangerous Commands"), description: styling_addDescriptionDiv("Hides Dangerous Commands, such as 'Delete Children'."), defaultValue: true, displayDuringSetup: false, required: false)
input(name: "disableCSS", type: "bool", title: styling_addTitleDiv("Disable CSS"), description: styling_addDescriptionDiv("CSS makes the driver more user friendly. Disable the use of CSS in the driver by enabling this. Does NOT affect HE resource usage either way."), defaultValue: false, displayDuringSetup: false, required: false)
// END: getDefaultMetadataPreferencesLast()
}
// BEGIN:getMetadataCustomizationMethods()
metaDataExporter()
if(isCSSDisabled() == false) {
preferences {
input(name: "hiddenSetting", description: "" + getDriverCSSWrapper(), title: "None", displayDuringSetup: false, type: "paragraph", element: "paragraph")
}
}
// END: getMetadataCustomizationMethods()
}
// BEGIN:getDeviceInfoFunction()
String getDeviceInfoByName(infoName) {
Map deviceInfo = ['name': 'Tasmota - Universal Parent', 'namespace': 'tasmota', 'author': 'Markus Liljergren', 'vid': 'generic-switch', 'filename': 'tasmota-universal-parent', 'importUrl': 'https://raw.githubusercontent.com/markus-li/Hubitat/development/drivers/expanded/tasmota-universal-parent-expanded.groovy']
return(deviceInfo[infoName])
}
// END: getDeviceInfoFunction()
// BEGIN:getHelperFunctions('device-configurations')
TreeMap getDeviceConfigurations() {
List deviceConfigurations = [
[typeId: 'sonoff-basic-r3',
name: 'Sonoff Basic R3',
module: 1,
installCommands: [["SetOption81", "0"]],
deviceLink: 'https://templates.blakadder.com/sonoff_basic_R3.html'],
[typeId: 'tuyamcu-ce-wf500d-dimmer',
name: 'TuyaMCU CE Smart Home WF500D Dimmer',
template: '{"NAME":"CE WF500D","GPIO":[255,255,255,255,255,255,0,0,255,108,255,107,255],"FLAG":0,"BASE":54}',
installCommands: [["SetOption66", "0"],
],
deviceLink: 'https://templates.blakadder.com/ce_smart_home-WF500D.html'],
[typeId: 'ce-la-2-w3-wall-outlet',
name: 'CE Smart Home LA-2-W3 Wall Outlet',
template: '{"NAME":"CE LA-2-W3","GPIO":[255,255,255,255,157,17,0,0,21,255,255,255,255],"FLAG":15,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/ce_smart_home_LA-2-W3.html'],
[typeId: 'ce-lq-2-w3-wall-outlet',
name: 'CE Smart Home LQ-2-W3 Wall Outlet',
template: '{"NAME":"CE LQ-2-W3","GPIO":[255,255,255,255,255,17,255,255,21,255,255,255,255],"FLAG":15,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/ce_smart_home_LQ-2-W3.html'],
[typeId: 'ce-la-wf7-pm-plug',
name: 'CE Smart Home LA-WF7 Power Monitor Plug',
template: '{"NAME":"CESmartHLA-WF7","GPIO":[0,56,0,17,134,132,0,0,131,57,21,0,0],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/ce_smart_home_LA-WF7.html'],
[typeId: 'mirebella-genio-i002741-light',
name: 'Mirabella Genio I002741 RGB+CCT Light',
template: '{"NAME":"GenioDLightRGB","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/mirabella_genio_I002741.html'],
[typeId: 'mirebella-genio-i002742-light',
name: 'Mirabella Genio I002742 CCT Light',
template: '{"NAME":"GenioDLightCCT","GPIO":[0,0,0,0,0,0,0,0,47,0,37,0,0],"FLAG":0,"BASE":48}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/mirabella_genio_I002742.html'],
[typeId: 'awp02l-n-plug',
name: 'AWP02L-N Plug',
template: '{"NAME":"AWP02L-N","GPIO":[57,0,56,0,0,0,0,0,0,17,0,21,0],"FLAG":1,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/hugoai_awp02l-n.html'],
[typeId: 'cyyltf-bifans-j23-plug',
name: 'CYYLTF BIFANS J23 Plug',
template: '{"NAME":"CYYLTF J23","GPIO":[56,0,0,0,0,0,0,0,21,17,0,0,0],"FLAG":1,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/cyyltd_bifans_J23.html'],
[typeId: 'gosund-wp3-plug',
name: 'Gosund WP3 Plug',
template: '{"NAME":"Gosund WP3","GPIO":[0,0,0,0,17,0,0,0,56,57,21,0,0],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/gosund_wp3.html'],
[typeId: 'sk03-pm-outdoor-plug',
name: 'SK03 Power Monitor Outdoor Plug',
template: '{"NAME":"SK03 Outdoor","GPIO":[17,0,0,0,133,132,0,0,131,57,56,21,0],"FLAG":0,"BASE":57}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/SK03_outdoor.html'],
[typeId: 'aoycocr-x10s-pm-plug',
name: 'Aoycocr X10S Power Monitor Plug',
template: '{"NAME":"Aoycocr X10S","GPIO":[56,0,57,0,21,134,0,0,131,17,132,0,0],"FLAG":0,"BASE":45}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/aoycocr_X10S.html'],
[typeId: 'brilliant-20699-rgbw-bulb',
name: 'Brilliant 20699 800lm RGBW Bulb',
template: '{"NAME":"Brilliant20699","GPIO":[0,0,0,0,141,140,0,0,37,142,0,0,0],"FLAG":0,"BASE":18}',
installCommands: [["WebLog", "2"]],
deviceLink: 'https://templates.blakadder.com/brilliant_20699.html'],
[typeId: 'sonoff-sv',
name: 'Sonoff SV',
template: '{"NAME":"Sonoff SV","GPIO":[17,255,0,255,255,255,0,0,21,56,255,0,0],"FLAG":1,"BASE":3}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/sonoff_SV.html'],
[typeId: 'sonoff-th',
name: 'Sonoff TH',
template: '{"NAME":"Sonoff TH","GPIO":[17,255,0,255,255,0,0,0,21,56,255,0,0],"FLAG":0,"BASE":4}',
installCommands: [["TempRes", (tempRes == '' || tempRes == null ? "1" : tempRes)]],
deviceLink: 'https://templates.blakadder.com/sonoff_TH.html'],
[typeId: 'sonoff-pow',
name: 'Sonoff POW',
template: '{"NAME":"Sonoff Pow","GPIO":[17,0,0,0,0,130,0,0,21,132,133,52,0],"FLAG":0,"BASE":6}',
installCommands: [["SetOption81", "1"], ["LedPower", "1"], ["LedState", "8"]],
deviceLink: 'https://templates.blakadder.com/sonoff_Pow.html'],
[typeId: 'sonoff-s31',
name: 'Sonoff S31',
template: '{"NAME":"Sonoff S31","GPIO":[17,145,0,146,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":41}',
installCommands: [["SetOption81", "1"], ["LedPower", "1"], ["LedState", "8"]],
deviceLink: 'https://templates.blakadder.com/sonoff_S31.html'],
[typeId: 'sonoff-ifan02',
name: 'Sonoff iFan02',
module: 44,
installCommands: [['Rule1', '0']],
deviceLink: 'https://templates.blakadder.com/sonoff_ifan02.html'],
[typeId: 'sonoff-ifan03-no_beep-m71',
name: 'Sonoff iFan03 (No Beep)',
module: 71,
installCommands: [["SetOption67", "0"], ['Rule1', '0']],
deviceLink: 'https://templates.blakadder.com/sonoff_ifan03.html'],
[typeId: 'sonoff-ifan03-beep-m71',
name: 'Sonoff iFan03 (Beep)',
module: 71,
installCommands: [["SetOption67", "1"],
['Rule1', 'ON Fanspeed#Data>=1 DO Buzzer %value%; ENDON ON Fanspeed#Data==0 DO Buzzer 1; ENDON'],
['Rule1', '1']],
deviceLink: 'https://templates.blakadder.com/sonoff_ifan03.html'],
[typeId: 'treatlife-ds01-dimmer',
name: 'TreatLife DS01 Dimmer ',
template: '{"NAME":"TL DS01 Dimmer","GPIO":[0,107,0,108,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}',
installCommands: [["TuyaMCU", "21,2"],
["DimmerRange", "150,1000"]],
deviceLink: 'https://templates.blakadder.com/treatlife_DS01.html'],
[typeId: 'deta-6911ha-switch',
name: 'Deta 6911HA Switch',
template: '{"NAME":"Deta 1G Switch","GPIO":[0,0,0,0,157,0,0,0,0,21,0,0,90],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/deta_6911HA.html'],
[typeId: 'deta-6912ha-switch',
name: 'Deta 6912HA Switch',
template: '{"NAME":"DETA 2G Switch","GPIO":[0,0,0,0,157,0,0,0,91,21,22,0,90],"FLAG":0,"BASE":18}',
installCommands: [['Rule1', 'on system#boot do Backlog LedPower 1; LedState 0; LedPower 1; LedState 8; endon'],
['Rule1', '1']],
deviceLink: 'https://templates.blakadder.com/deta_6912HA.html'],
[typeId: 'deta-6903ha-switch',
name: 'Deta 6903HA Switch',
template: '{"NAME":"DETA 3G Switch","GPIO":[157,0,0,92,91,21,0,0,23,0,22,0,90],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/deta_6903HA.html'],
[typeId: 'deta-6904ha-switch',
name: 'Deta 6904HA Switch',
template: '{"NAME":"Deta 4G Switch","GPIO":[157,0,0,19,18,21,0,0,23,20,22,24,17],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/deta_6904HA.html'],
[typeId: 'deta-6922ha-outlet',
name: 'Deta 6922HA Wall Outlet',
template: '{"NAME":"DETA 2G GPO","GPIO":[0,0,0,17,157,0,0,0,91,21,22,0,90],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/deta_6922HA.html'],
[typeId: 'lh-znb22-001-9w ',
name: 'Lohas ZN033 9W 810lm RGBCCT Bulb ',
template: '{"NAME":"Lohas RGBCW","GPIO":[0,0,0,0,38,37,0,0,41,39,40,0,0],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/lohas-ZN033-B22.html'],
[typeId: 'kmc-4-pm-plug',
name: 'KMC 4 Power Monitor Plug',
template: '{"NAME":"KMC 4 Plug","GPIO":[0,56,0,0,133,132,0,0,130,22,23,21,17],"FLAG":0,"BASE":36}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/kmc-4.html'],
[typeId: 'teckin-ss30-power-strip',
name: 'Teckin SS30 Power Strip',
template: '{"NAME":"Teckin SS30","GPIO":[52,255,255,57,29,17,0,0,31,30,32,255,25],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/teckin_ss30.html'],
[typeId: 'teckin-sp10-plug',
name: 'Teckin SP10 Plug',
template: '{"NAME":"Teckin SP10","GPIO":[255,255,56,255,255,255,0,0,255,17,255,21,255],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/teckin_SP10.html'],
[typeId: 'awp04l-pm-plug',
name: 'AWP04L Power Monitor Plug',
template: '{"NAME":"AWP04L","GPIO":[57,255,255,131,255,134,0,0,21,17,132,56,255],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/awp04l.html'],
[typeId: 'dd001-mini-ir-v08-rgb-led-controller-no-ir',
name: 'DD001-MINI(G)-IR-V08 RGB LED Controller (no IR)',
template: '{"NAME":"DD001-NOIR-RGB","GPIO":[0,0,0,0,37,0,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}',
installCommands: [["WebLog", "2"]],
deviceLink: 'https://templates.blakadder.com/DD001-MINIG-IR-V08.html'],
[typeId: 'sonoff-4ch-pro',
name: 'Sonoff 4CH Pro',
template: '{"NAME":"Sonoff 4CH Pro","GPIO":[17,255,255,255,23,22,18,19,21,56,20,24,0],"FLAG":0,"BASE":23}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/sonoff_4CH_Pro.html'],
[typeId: 'unbranded-rgbwwcw-controller-type-1',
name: 'Unbranded RGBWWCW Controller (Type 1)',
template: '{"NAME":"CtrlRGBWWCW-T1","GPIO":[17,0,0,0,0,40,0,0,38,39,37,41,0],"FLAG":0,"BASE":18}',
installCommands: [["WebLog", "2"]],
deviceLink: ''],
[typeId: 'tuyamcu-touch-switch-1-button',
name: 'TuyaMCU Touch Switch - 1 button',
module: 54,
installCommands: [["TuyaMCU", "11,1"], ["TuyaMCU", "12,0"],
["TuyaMCU", "13,0"], ["TuyaMCU", "14,0"]],
deviceLink: ''],
[typeId: 'tuyamcu-lucci-connect-remote-as-switches',
name: 'TuyaMCU Lucci Connect Remote',
template: '{"NAME":"Lucci Fan","GPIO":[0,0,0,0,0,0,0,0,0,108,0,107,0],"FLAG":0,"BASE":54}',
installCommands: [["TuyaMCU", "11,102"], ["TuyaMCU", "12,1"], ["TuyaMCU", "13,0"],
["TuyaMCU", "14,0"], ["TuyaMCU", "15,0"], ["TuyaMCU", "21,50"],
["Rule1", "on TuyaReceived#Data=55AA00070005020400010012 do dimmer 1 endon on TuyaReceived#Data=55AA00070005020400010113 do dimmer 2 endon on TuyaReceived#Data=55AA00070005020400010214 do dimmer 3 endon on Dimmer#State=1 do TuyaSend4 2,0 endon on Dimmer#State=2 do TuyaSend4 2,1 endon on Dimmer#State=3 do TuyaSend4 2,2 endon"],
["Rule1", "1"]],
deviceLink: 'https://templates.blakadder.com/luci-connect-remote-control.html'],
[typeId: 'tuyamcu-touch-switch-2-button',
name: 'TuyaMCU Touch Switch - 2 buttons',
module: 54,
installCommands: [["TuyaMCU", "11,1"], ["TuyaMCU", "12,2"],
["TuyaMCU", "13,0"], ["TuyaMCU", "14,0"]],
deviceLink: ''],
[typeId: 'tuyamcu-touch-switch-3-button',
name: 'TuyaMCU Touch Switch - 3 buttons',
module: 54,
installCommands: [["TuyaMCU", "11,1"], ["TuyaMCU", "12,2"],
["TuyaMCU", "13,3"], ["TuyaMCU", "14,0"]],
deviceLink: ''],
[typeId: 'tuyamcu-touch-switch-4-button',
name: 'TuyaMCU Touch Switch - 4 buttons',
module: 54,
template: '',
installCommands: [["TuyaMCU", "11,1"], ["TuyaMCU", "12,2"],
["TuyaMCU", "13,3"], ["TuyaMCU", "14,4"]],
deviceLink: ''],
[typeId: 'feit-electric-dimmer',
name: 'Feit Electric Dimmer',
module: 54,
installCommands: [["TuyaMCU", "11,1"], ["TuyaMCU", "21,2"],
["DimmerRange", "10,1000"]],
deviceLink: 'https://templates.blakadder.com/feit_electric-DIM-WIFI.html'],
[typeId: 'sonoff-powr2',
name: 'Sonoff POW R2',
template: '{"NAME":"Sonoff Pow R2","GPIO":[17,145,0,146,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":43}',
installCommands: [["SetOption81", "1"], ["LedPower", "1"], ["LedState", "8"]],
deviceLink: 'https://templates.blakadder.com/sonoff_Pow_R2.html'],
[typeId: 'sonoff-s20',
name: 'Sonoff S20',
template: '{"NAME":"Sonoff S20","GPIO":[17,255,255,255,0,0,0,0,21,56,0,0,0],"FLAG":0,"BASE":8}',
installCommands: [["SetOption81", "1"], ["LedPower", "1"], ["LedState", "8"]],
deviceLink: 'https://templates.blakadder.com/sonoff_S20.html'],
[typeId: 'sonoff-s26',
name: 'Sonoff S26',
template: '{"NAME":"Sonoff S26","GPIO":[17,255,255,255,0,0,0,0,21,158,0,0,0],"FLAG":0,"BASE":8}',
installCommands: [["SetOption81", "1"]],
deviceLink: 'https://templates.blakadder.com/sonoff_S26.html'],
[typeId: 'sonoff-mini',
name: 'Sonoff Mini',
template: '{"NAME":"Sonoff Mini","GPIO":[17,0,0,0,9,0,0,0,21,56,0,0,255],"FLAG":0,"BASE":1}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/sonoff_mini.html'],
[typeId: 'sonoff-basic',
name: 'Sonoff Basic',
module: 1,
installCommands: [["SetOption81", "1"]],
deviceLink: 'https://templates.blakadder.com/sonoff_basic.html'],
[typeId: 's120-plug' ,
name: 'S120 USB Charger Plug',
template: '{"NAME":"S120 Plug","GPIO":[0,0,0,0,0,21,0,0,0,52,90,0,0],"FLAG":0,"BASE":18}',
installCommands: [["SetOption81", "1"]],
deviceLink: 'https://templates.blakadder.com/brilliantsmart_20676.html'],
[typeId: 's120-plug-bmp280' ,
name: 'S120 USB Charger Plug + BMP280',
template: '{"NAME":"S120THPPlug","GPIO":[0,6,0,5,0,21,0,0,0,52,90,0,0],"FLAG":0,"BASE":18}',
installCommands: [["SetOption81", "1"]],
deviceLink: 'https://templates.blakadder.com/brilliantsmart_20676.html'],
[typeId: 'globe-34207-bulb' ,
name: 'Globe 34207 800lm RGBCCT Bulb',
template: '{"NAME":"GlobeRGBWW","GPIO":[0,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/globe-34207.html'],
[typeId: 'brilliantsmart-20676-plug' ,
name: 'BrilliantSmart 20676 USB Charger Plug',
template: '{"NAME":"Brilliant20676","GPIO":[0,0,0,0,0,21,0,0,0,52,90,0,0],"FLAG":0,"BASE":18}',
installCommands: [["SetOption81", "1"]],
deviceLink: 'https://templates.blakadder.com/brilliantsmart_20676.html'],
[typeId: 'brilliantsmart-20741-bulb' ,
name: 'BrilliantSmart 20741 9W 750lm RGBW Bulb',
template: '{"NAME":"BS-20741-RGBW","GPIO":[0,0,0,0,37,40,0,0,38,0,39,0,0],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/brilliantsmart_20741.html'],
[typeId: 'brilliant-bl20925-pm-plug',
name: 'Brilliant Lighting BL20925 PM Plug',
template: '{"NAME":"BL20925","GPIO":[0,0,0,17,133,132,0,0,131,158,21,0,0],"FLAG":0,"BASE":52}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/brilliant_BL20925.html'],
[typeId: 'deta-6930ha-plug',
name: 'Deta 6930HA Plug',
template: '{"NAME":"Deta6930HAPlug","GPIO":[0,17,0,0,0,0,0,0,0,56,21,0,0],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/deta_6930HA.html'],
[typeId: 'prime-ccrcwfii113pk-plug',
name: 'Prime CCRCWFII113PK Plug',
template: '{"NAME":"PrimeCCRC13PK","GPIO":[0,0,0,0,57,56,0,0,21,122,0,0,0],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/prime_CCRCWFII113PK.html'],
[typeId: 'ykyc-wj1y0-10a',
name: 'YKYC-WJ1Y0-10A PM Plug',
template: '{"NAME":"YKYC-001PMPlug","GPIO":[0,17,0,57,133,132,0,0,130,56,21,0,0],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: ''],
[typeId: 'merkury-mi-bw210-999w',
name: 'Merkury MI-BW210-999W',
template: '{"NAME":"MI-BW210-999W","GPIO":[0,0,0,0,140,37,0,0,142,38,141,0,0],"FLAG":0,"BASE":48}',
installCommands: [],
deviceLink: ''],
[typeId: 'lumary-rgbcct-led-strip',
name: 'Lumary RGBCCT LED Strip',
template: '{"NAME":"Lumary LED","GPIO":[17,0,0,0,37,40,0,0,38,41,39,0,0],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/lumary_led_strip.html'],
[typeId: 'xs-ssa06-plug',
name: 'XS-SSA06 Plug with RGB',
template: '{"NAME":"XS-SSA06-RGB","GPIO":[37,0,38,0,0,39,0,0,0,90,0,21,0],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/XS-SSA06.html'],
[typeId: 'tuyamcu-wifi-dimmer',
name: 'TuyaMCU Wifi Dimmer',
module: 54,
installCommands: [["SetOption66", "0"],
],
deviceLink: ''],
[typeId: 'zigbee-controller-default' ,
name: 'Zigbee Controller (default pinout)',
template: '{"NAME":"Zigbee","GPIO":[0,0,0,0,0,0,0,0,0,166,0,165,0],"FLAG":0,"BASE":18}',
installCommands: [["SerialLog", "0"],
],
deviceLink: 'https://tasmota.github.io/docs/#/Zigbee'],
[typeId: 'unbranded-rgb-controller-with-ir-type-1' ,
name: 'Unbranded RGB Controller with IR (Type 1)',
template: '{"NAME":"RGB Controller","GPIO":[0,0,0,0,0,38,0,0,39,51,0,37,0],"FLAG":15,"BASE":18}',
installCommands: [["WebLog", "2"]],
deviceLink: ''],
[typeId: 'sonoff-4ch',
name: 'Sonoff 4CH',
template: '{"NAME":"Sonoff 4CH","GPIO":[17,255,255,255,23,22,18,19,21,56,20,24,0],"FLAG":0,"BASE":7}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/sonoff_4CH.html'],
[typeId: 'sonoff-4ch-pro-r2',
name: 'Sonoff 4CH Pro (R2)',
template: '{"NAME":"Sonoff 4CH Pro","GPIO":[17,255,255,255,23,22,18,19,21,56,20,24,0],"FLAG":0,"BASE":23}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/sonoff_4CH_Pro.html'],
[typeId: 'nedis-ir-bridge',
name: 'Nedis IR Bridge',
template: '{"NAME":"Nedis IR Bridge","GPIO":[255,255,255,255,56,51,0,0,0,17,8,0,0],"FLAG":0,"BASE":62}',
installCommands: [['SerialLog', '0']],
deviceLink: 'https://templates.blakadder.com/nedis_WIFIRC10CBK.html'],
[typeId: 'luminea-zx-2844-rgbw-led-controller',
name: 'Luminea ZX-2844 RGBW LED Controller ',
template: '{"NAME":"Luminea ZX-284","GPIO":[40,0,0,0,0,39,0,0,38,17,37,0,0],"FLAG":0,"BASE":18}',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/luminea_zx-2844.html'],
[typeId: 'tuyamcu-znsn-wifi-curtain-wall-panel',
comment: 'NOT GENERIC - read the instructions',
name: 'TuyaMCU ZNSN Wifi Curtain Wall Panel',
template: '{"NAME":"ZNSN Curtain","GPIO":[0,107,0,108,21,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54}',
installCommands: [["WebLog", "2"],
['SetOption66', "1"],
['SetOption80', "1"],
["PulseTime1", "0"],
["PulseTime2", "0"],
["Interlock", "1,2"],
["Interlock", "ON"],
["ShutterMotorDelay", "4.5"],
["ShutterOpenDuration", "10"],
["ShutterCloseDuration", "11.2"],
["Var1", "ShutterClose1"],
["Var2", "ShutterStop1"],
["Var3", "ShutterOpen1"],
["setoption34", "50"],
["Rule1", "ON Power1#state=1 DO Backlog var3 var3; var2 ShutterStop1; TuyaSend4 101,0 ENDON "],
["Rule1", "+ ON Power1#state=0 DO Backlog var2 var2; TuyaSend4 101,1; var1 ShutterClose1; var3 ShutterOpen1; ENDON "],
["Rule1", "+ ON Power2#state=0 DO Backlog var2 var2; TuyaSend4 101,1; var1 ShutterClose1; var3 ShutterOpen1; ENDON "],
["Rule1", "+ ON Power2#state=1 DO Backlog var1 var1; var2 ShutterStop1; TuyaSend4 101,2 ENDON "],
["Rule1", "+ ON ShutterStop#Data DO Backlog var2 var2; TuyaSend4 101,1; var1 ShutterClose1; var3 ShutterOpen1; ENDON "],
["Rule1", "+ ON Shutter1#Position DO var4 %value% ENDON ON Event#Close0 DO Backlog var2 var2; TuyaSend4 101,1; ENDON "],
["Rule1", "1"],
["Rule2", "ON TuyaReceived#Data=55AA00070005650400010277 DO backlog var1 ShutterClose1; %var1%; ENDON "],
["Rule2", "+ ON System#Init DO Backlog setoption34 50; var1 ShutterClose1; var2 ShutterStop1; var3 ShutterOpen1; ENDON "],
["Rule2", "+ ON TuyaReceived#Data=55AA00070005020400010214 DO backlog var2 ShutterStop1; %var2%; ENDON "],
["Rule2", "+ ON TuyaReceived#Data=55AA00070005650400010176 DO backlog var3 ShutterOpen1; %var3%; ENDON "],
["Rule2", "+ ON Event#Open100 DO Backlog var2 var2; TuyaSend4 101,1; ENDON "],
["Rule2", "+ ON ShutterOpen#Data=100 DO Event Open%var4% ENDON ON ShutterClose#Data=0 DO Event Close%var4% ENDON "],
["Rule2", "1"],
],
deviceLink: '',
open: ["TuyaSend4", "101,0"],
stop: ["TuyaSend4", "101,1"],
close: ["TuyaSend4", "101,2"],],
[typeId: 'mj-sd02-dimmer-switch',
comment: 'WITHOUT power status LED active by design',
name: 'Martin Jerry MJ-SD02 Dimmer Switch',
template: '{"NAME":"MJ-SD02","GPIO":[19,18,0,33,34,32,255,255,31,37,30,126,29],"FLAG":15,"BASE":18}',
installCommands: [["WebLog", "2"],
['SerialLog', '0'],
['setoption3', '1'],
['setoption1', '1'],
['setoption32', '8'],
['buttontopic', '0'],
['Rule1', 'on Button3#state=2 do dimmer + endon on Button2#state=2 do dimmer - endon '],
['Rule1', '+ on Button2#state=3 do dimmer 20 endon on Button3#state=3 do dimmer 100 endon '],
['Rule1', '+ on Button1#state=2 do power1 2 endon on Button1#state=3 do power1 0 endon'],
['Rule1', '1']],
deviceLink: ''],
[typeId: 'mj-sd02-dimmer-switch-led',
comment: 'WITH power status LED active by design',
name: 'Martin Jerry MJ-SD02 Dimmer Switch',
template: '{"NAME":"MJ-SD02-LED","GPIO":[19,18,0,33,56,32,255,255,31,37,30,126,29],"FLAG":15,"BASE":18}',
installCommands: [["WebLog", "2"],
['SerialLog', '0'],
['setoption3', '1'],
['setoption1', '1'],
['setoption32', '8'],
['buttontopic', '0'],
['LedPower', '1'],
['SetOption31', '0'],
['Rule1', 'on Button3#state=2 do dimmer + endon on Button2#state=2 do dimmer - endon '],
['Rule1', '+ on Button2#state=3 do dimmer 20 endon on Button3#state=3 do dimmer 100 endon '],
['Rule1', '+ on Button1#state=2 do power1 2 endon on Button1#state=3 do power1 0 endon'],
['Rule1', '1']],
deviceLink: ''],
[typeId: 'maxcio-diffuser-v1',
comment: 'REQUIRES "Use Alternate Color command in Tasmota" to be set!',
name: 'Maxcio Diffuser Wood Grain (v1)',
template: '{"NAME":"MaxcioDiffuser","GPIO":[0,107,0,108,21,0,0,0,37,38,39,28,0],"FLAG":0,"BASE":54}',
installCommands: [["WebLog", "2"],
['SerialLog', '0'],
['setoption20', '1'],
['Rule1', 'ON Var1#State DO backlog tuyasend3 8,%value%00ffff00; color %value%; rule2 0; power1 1; rule2 1; ENDON ON Scheme#Data=0 DO TuyaSend4 6,0 ENDON ON Scheme#Data>0 DO TuyaSend4 6,1 ENDON ON TuyaReceived#Data=55AA03070005050100010116 DO power1 1 ENDON ON TuyaReceived#Data=55AA03070005010100010011 DO backlog rule2 0; power2 0; rule2 1; power3 %var2%; var2 1; ENDON ON TuyaReceived#Data=55AA03070005010100010112 DO backlog rule2 0; power2 1; rule2 1; var2 0; power3 0; ENDON'],
['Rule2', 'ON Power1#State DO tuyasend1 5,%value% ENDON ON Power2#State=0 DO tuyasend1 1,0 ENDON ON Power2#State=1 DO backlog var2 1; tuyasend1 1,1; ENDON'],
['Rule3', 'ON TuyaReceived#Data=55AA03070005050100010015 DO power1 0 ENDON'],
['Rule1', '1'],
['Rule2', '1'],
['Rule3', '1']],
deviceLink: 'https://templates.blakadder.com/maxcio_400ml_diffuser.html'],
[typeId: 'sonoff-rf-bridge-parent' ,
notForUniversal: true,
comment: 'Functional - Need feedback',
name: '',
template: '',
installCommands: [],
deviceLink: 'https://templates.blakadder.com/sonoff_RF_bridge.html'],
[typeId: 'rflink-parent' ,
notForUniversal: true,
comment: 'Functional - Need feedback',
name: '',
template: '',
installCommands: [],
deviceLink: 'http://www.rflink.nl/blog2/wiring'],
[typeId: '01generic-device',
comment: 'Works with most devices' ,
name: 'Generic Device',
installCommands: [],
deviceLink: ''],
[typeId: '01generic-rgb-rgbw-controller-bulb-dimmer',
comment: 'RGB+WW+CW should all work properly',
name: 'Generic RGB/RGBW Controller/Bulb/Dimmer',
template: '',
installCommands: [["WebLog", "2"]],
deviceLink: ''],
[typeId: '01generic-thp-device' ,
name: 'Generic Temperature/Humidity/Pressure Device',
template: '',
installCommands: [["TempRes", (tempRes == '' || tempRes == null ? "1" : tempRes)]],
deviceLink: ''],
]
TreeMap deviceConfigurationsMap = [:] as TreeMap
deviceConfigurations.each{
deviceConfigurationsMap[it["typeId"]] = it
}
return deviceConfigurationsMap
}
def getDeviceConfiguration(String typeId) {
TreeMap deviceConfigurationsMap = getDeviceConfigurations()
try{
return deviceConfigurationsMap[typeId]
} catch(e) {
log.warn "Failed to retrieve Device Configuration '$typeId': $e"
return null
}
}
def getDeviceConfigurationsAsListOption() {
TreeMap deviceConfigurationsMap = getDeviceConfigurations()
def items = []
deviceConfigurationsMap.sort({ a, b -> a.key <=> b.key }).each { k, v ->
def label = v["name"]
if(v.containsKey("comment") && v["comment"].length() > 0) {
label += " (${v["comment"]})"
}
if(!(v.containsKey("notForUniversal") && v["notForUniversal"] == true)) {
items << ["${v["typeId"]}":"$label"]
}
}
return items
}
// END: getHelperFunctions('device-configurations')
/* These methods are unique to each driver */
def installed() {
logging("installed()", 100)
tasmota_installedPreConfigure()
configurePresence()
}
def initialize() {
logging("initialize()", 100)
generalInitialize()
configurePresence()
}
def updated() {
logging("updated()", 100)
setDisableCSS(disableCSS)
configurePresence()
unschedule("updatePresence")
unschedule("tasmota_updatePresence")
updateNeededSettings()
refresh()
}
def configure() {
generalInitialize()
updateNeededSettings()
}
def getDriverCSS() {
r = ""
r += '''
/*form[action*="preference"]::before {
color: green;
content: "Hi, this is my content"
}
form[action*="preference"] div[for^=preferences] {
color: blue;
}*/
div#stateComment {
display: inline;
}
/*div#stateComment:after {
color: red;
display: inline;
visibility: visible;
position: absolute;
bottom: 150%;
left: 400%;
white-space: nowrap;
}*/
div#stateComment:after {
color: #382e2b;
visibility: visible;
position: relative;
white-space: nowrap;
display: inline;
}
/*div#stateComment:after {
color: #382e2b;
display: inline;
visibility: visible;
position: fixed;
left: 680px;
white-space: nowrap;
top: 95px;
}*/
/*
div#stateComment:after {
color: #5ea767;
display: inline;
visibility: visible;
position: absolute;
left: 120px;
white-space: nowrap;
bottom: -128px;
height: 36px;
vertical-align: middle;
}*/
div#stateCommentInside {
display: none;
}
li[id*='stateCommentInside'] {
/*visibility: hidden;*/
/*position: absolute;*/
display: list-item;
}
.property-value {
overflow-wrap: break-word;
}
'''
return r
}
def refresh() {
def metaConfig = tasmota_refresh(metaConfig=null)
metaConfig = setStateVariablesToHide(['mac'], metaConfig=metaConfig)
logging("hideExtended=$hideExtended, hideAdvanced=$hideAdvanced", 1)
if(hideExtended == null || hideExtended == true) {
metaConfig = setPreferencesToHide(['hideAdvanced', 'ipAddress', 'override', 'useIPAsID', 'telePeriod', 'invertPowerNumber', 'useAlternateColorCommand'], metaConfig=metaConfig)
}
if(hideExtended == null || hideExtended == true || hideAdvanced == null || hideAdvanced == true) {
metaConfig = setPreferencesToHide(['disableModuleSelection', 'port', 'disableCSS', 'moduleNumber'], metaConfig=metaConfig)
}
if(hideDangerousCommands == null || hideDangerousCommands == true) {
metaConfig = setCommandsToHide(['deleteChildren', 'initialize'], metaConfig=metaConfig)
} else {
metaConfig = setCommandsToHide(['initialize'], metaConfig=metaConfig)
}
if(deviceConfig == null) deviceConfig = "01generic-device"
deviceConfigMap = getDeviceConfiguration(deviceConfig)
logging("deviceConfigMap=$deviceConfigMap", 1)
try{
if(deviceConfigMap.containsKey('comment') &&
deviceConfigMap['comment'] != null &&
deviceConfigMap['comment'].length() > 0) {
logging("Settings state.comment...", 1)
setStateCommentInCSS(deviceConfigMap['comment'], metaConfig=metaConfig)
state.comment = "
"
} else {
logging("Hiding state.comment...", 1)
state.comment = ""
metaConfig = setStateVariablesToHide(['comment'], metaConfig=metaConfig)
}
} catch(e2) {
log.warn e2
metaConfig = setStateVariablesToHide(['comment'], metaConfig=metaConfig)
}
metaConfig = setDatasToHide(["preferences", "metaConfig"], metaConfig=metaConfig)
}
/* The parse(description) function is included and auto-expanded from external files */
void parse(String description) {
// BEGIN:getGenericTasmotaNewParseHeader()
Map descMap = tasmota_parseDescriptionAsMap(description)
String body = null
//logging("descMap: ${descMap}", 0)
boolean missingChild = false
if (state.mac != descMap["mac"]) {
logging("Mac address of device found ${descMap["mac"]}", 10)
state.mac = descMap["mac"]
}
sendlastCheckinEvent(minimumMinutesToRepeat=55)
tasmota_prepareDNI()
if (descMap["body"] && descMap["body"] != "T04=") body = new String(descMap["body"].decodeBase64())
if (body && body != "") {
if(body.startsWith("{") || body.startsWith("[")) {
boolean log99 = logging("========== Parsing Report ==========", 99)
JsonSlurper slurper = new JsonSlurper()
Map result = slurper.parseText(body)
//logging("result: ${result}",0)
// END: getGenericTasmotaNewParseHeader()
missingChild = parseResult(result, missingChild)
// BEGIN:getGenericTasmotaNewParseFooter()
result = null
} else {
}
}
if(missingChild == true) {
log.warn "Missing a child device, run the Refresh command from the device page!"
if(state.installing != "1") {
logging("Initiating child device installation...", 100)
state.installing = "1"
runIn(120, "clearInstalling")
tasmota_getAction(tasmota_getCommandString("Status", "0"), callback="tasmota_parseConfigureChildDevices")
}
}
if (device.currentValue("ip") == null) {
String curIP = getDataValue("ip")
logging("Setting IP from Data: $curIP", 1)
sendEvent(name: 'ip', value: curIP, isStateChange: false)
sendEvent(name: "ipLink", value: "$curIP", isStateChange: false)
}
descMap = null
body = null
// END: getGenericTasmotaNewParseFooter()
}
boolean parseResult(Map result) {
boolean missingChild = false
missingChild = parseResult(result, missingChild)
return missingChild
}
void parseJSON(String jsonData) {
boolean missingChild = false
JsonSlurper jsonSlurper = new JsonSlurper()
parseResult(jsonSlurper.parseText(jsonData), missingChild)
jsonSlurper = null
}
boolean parseResult(Map result, boolean missingChild) {
boolean log99 = logging("parseResult: $result", 99)
logging("parseResult: $result", 100)
// BEGIN:getTasmotaNewParserForStatusSTS()
if (result.containsKey("StatusSTS")) {
logging("StatusSTS: $result.StatusSTS",99)
result << result.StatusSTS
}
// END: getTasmotaNewParserForStatusSTS()
// BEGIN:getTasmotaNewParserForParentSwitch()
if (result.containsKey("POWER") == true && result.containsKey("POWER1") == false) {
logging("parser: POWER (child): $result.POWER",1)
missingChild = callChildParseByTypeId("POWER1", [[name:"switch", value: result.POWER.toLowerCase()]], missingChild)
} else {
String currentPower = ""
(1..16).each {i->
currentPower = "POWER$i"
if(result.containsKey(currentPower) == true) {
if(i < 3 && invertPowerNumber == true) {
if(i == 1) {
currentPower = "POWER2"
} else {
currentPower = "POWER1"
}
}
logging("parser: $currentPower (original: POWER$i): ${result."POWER$i"}",1)
missingChild = callChildParseByTypeId("$currentPower", [[name:"switch", value: result."POWER$i".toLowerCase()]], missingChild)
}
}
}
// END: getTasmotaNewParserForParentSwitch()
// BEGIN:getTasmotaNewParserForDimmableDevice()
if(true) {
com.hubitat.app.ChildDeviceWrapper childDevice = tasmota_getChildDeviceByActionType("POWER1")
if(result.containsKey("Dimmer")) {
def dimmer = result.Dimmer
logging("Dimmer: ${dimmer}", 1)
state.level = dimmer
if(childDevice?.currentValue('level') != dimmer ) missingChild = callChildParseByTypeId("POWER1", [[name: "level", value: dimmer]], missingChild)
}
if(result.containsKey("TuyaReceived") && result.TuyaReceived.containsKey("Data")) {
if(result.TuyaReceived.Data != "55AA000000010101") {
missingChild = callChildParseByTypeId("POWER1", [[name: "tuyaData", value: result.TuyaReceived.Data]], missingChild)
}
}
if(log99 == true && result.containsKey("Wakeup")) {
logging("Wakeup: ${result.Wakeup}", 99)
}
}
// END: getTasmotaNewParserForDimmableDevice()
// BEGIN:getTasmotaNewParserForRGBWDevice()
if(true) {
com.hubitat.app.ChildDeviceWrapper childDevice = tasmota_getChildDeviceByActionType("POWER1")
String mode = "RGB"
if (result.containsKey("Color")) {
String color = result.Color
logging("Color: ${color}, size: ${result.Color.tokenize(",").size()}", 1)
if((color.length() > 6 && color.startsWith("000000")) ||
(result.Color.tokenize(",").size() > 3 && color.startsWith("0,0,0"))) {
mode = "CT"
}
state.colorMode = mode
if(childDevice?.currentValue('colorMode') != mode ) missingChild = callChildParseByTypeId("POWER1", [[name: "colorMode", value: mode]], missingChild)
}
if (result.containsKey("Scheme")) {
if(childDevice?.currentValue('effectNumber') != result.Scheme ) missingChild = callChildParseByTypeId("POWER1", [[name: "effectNumber", value: result.Scheme]], missingChild)
}
if (mode == "RGB" && result.containsKey("HSBColor")) {
List hsbColor = result.HSBColor.tokenize(",")
hsbColor[0] = Math.round((hsbColor[0] as Integer) / 3.6) as Integer
hsbColor[1] = hsbColor[1] as Integer
logging("hsbColor: ${hsbColor}", 1)
if(childDevice?.currentValue('hue') != hsbColor[0] ) missingChild = callChildParseByTypeId("POWER1", [[name: "hue", value: hsbColor[0]]], missingChild)
if(childDevice?.currentValue('saturation') != hsbColor[1] ) missingChild = callChildParseByTypeId("POWER1", [[name: "saturation", value: hsbColor[1]]], missingChild)
String colorName = rgbw_getColorNameFromHueSaturation(hsbColor[0], hsbColor[1])
if(childDevice?.currentValue('colorName') != colorName ) {
missingChild = callChildParseByTypeId("POWER1", [[name: "colorName", value: colorName]], missingChild)
}
} else if (result.containsKey("CT")) {
Integer t = Math.round(1000000/result.CT)
if(childDevice?.currentValue('colorTemperature') != t ) {
missingChild = callChildParseByTypeId("POWER1", [[name: "colorTemperature", value: t]], missingChild)
}
String colorName = rgbw_getColorNameFromTemperature(t)
if(childDevice?.currentValue('colorName') != colorName ) {
missingChild = callChildParseByTypeId("POWER1", [[name: "colorName", value: colorName]], missingChild)
}
logging("CT: $result.CT ($t)",99)
}
}
// END: getTasmotaNewParserForRGBWDevice()
// BEGIN:getTasmotaNewParserForFanMode()
if (result.containsKey("FanSpeed")) {
String speed = "off"
switch(result.FanSpeed) {
case "1":
speed = "low"
break
case "2":
speed = "medium"
break
case "3":
speed = "high"
break
}
logging("parser: FanSpeed: $result.FanSpeed, speed = $speed", 1)
missingChild = callChildParseByTypeId("FAN", [[name:"speed", value: speed]], missingChild)
}
// END: getTasmotaNewParserForFanMode()
// BEGIN:getTasmotaNewParserForBasicData()
if (result.containsKey("StatusNET")) {
logging("StatusNET: $result.StatusNET",99)
result << result.StatusNET
}
if (result.containsKey("StatusFWR")) {
logging("StatusFWR: $result.StatusFWR",99)
result << result.StatusFWR
}
if (result.containsKey("StatusPRM")) {
logging("StatusPRM: $result.StatusPRM",99)
result << result.StatusPRM
}
if (false && result.containsKey("Status")) {
logging("Status: $result.Status",99)
result << result.Status
}
if (result.containsKey("LoadAvg")) {
logging("LoadAvg: $result.LoadAvg",99)
}
if (log99 == true && result.containsKey("Sleep")) {
logging("Sleep: $result.Sleep",99)
}
if (log99 == true && result.containsKey("SleepMode")) {
logging("SleepMode: $result.SleepMode",99)
}
if (log99 == true && result.containsKey("Vcc")) {
logging("Vcc: $result.Vcc",99)
}
if (log99 == true && result.containsKey("Hostname")) {
logging("Hostname: $result.Hostname",99)
}
if (result.containsKey("IPAddress") && (override == false || override == null)) {
logging("IPAddress: $result.IPAddress",99)
sendEvent(name: "ip", value: "$result.IPAddress", isStateChange: false)
sendEvent(name: "ipLink", value: "$result.IPAddress", isStateChange: false)
updateDataValue("ip", "$result.IPAddress")
}
if (log99 == true && result.containsKey("WebServerMode")) {
logging("WebServerMode: $result.WebServerMode",99)
}
if (result.containsKey("Version")) {
logging("Version: $result.Version",99)
updateDataValue("firmware", result.Version)
}
if (result.containsKey("Module") && !result.containsKey("Version")) {
logging("Module: $result.Module",50)
sendEvent(name: "module", value: "$result.Module", isStateChange: false)
}
if (result.containsKey("NAME") && result.containsKey("GPIO") && result.containsKey("FLAG") && result.containsKey("BASE")) {
def n = result.toMapString()
n = n.replaceAll(', ',',')
n = n.replaceAll('\\[','{').replaceAll('\\]','}')
n = n.replaceAll('NAME:', '"NAME":"').replaceAll(',GPIO:\\{', '","GPIO":\\[')
n = n.replaceAll('\\},FLAG', '\\],"FLAG"').replaceAll('BASE', '"BASE"')
logging("Template: $n",50)
sendEvent(name: "templateData", value: "${n}", isStateChange: false)
}
if (result.containsKey("RestartReason")) {
log.warn("RestartReason: $result.RestartReason")
}
if (result.containsKey("TuyaMCU")) {
logging("TuyaMCU: $result.TuyaMCU",99)
sendEvent(name: "tuyaMCU", value: "$result.TuyaMCU", isStateChange: false)
}
if (log99 == true && result.containsKey("SetOption81")) {
logging("SetOption81: $result.SetOption81",99)
}
if (log99 == true && result.containsKey("SetOption113")) {
logging("SetOption113 (Hubitat enabled): $result.SetOption113",99)
}
if (result.containsKey("Uptime")) {
logging("Uptime: $result.Uptime",99)
state.uptime = result.Uptime
updateDataValue('uptime', result.Uptime)
}
// END: getTasmotaNewParserForBasicData()
// BEGIN:getTasmotaNewParserForEnergyMonitor()
if (result.containsKey("StatusSNS")) {
result << result.StatusSNS
}
if (result.containsKey("ENERGY")) {
if (result.ENERGY.containsKey("Total")) {
logging("Total: $result.ENERGY.Total kWh",99)
missingChild = callChildParseByTypeId("POWER1", [[name:"energyTotal", value:"$result.ENERGY.Total kWh"]], missingChild)
}
if (result.ENERGY.containsKey("Today")) {
logging("Today: $result.ENERGY.Today kWh",99)
missingChild = callChildParseByTypeId("POWER1", [[name:"energyToday", value:"$result.ENERGY.Today kWh"]], missingChild)
}
if (result.ENERGY.containsKey("Yesterday")) {
logging("Yesterday: $result.ENERGY.Yesterday kWh",99)
missingChild = callChildParseByTypeId("POWER1", [[name:"energyYesterday", value:"$result.ENERGY.Yesterday kWh"]], missingChild)
}
if (result.ENERGY.containsKey("Current")) {
logging("Current: $result.ENERGY.Current A",99)
def r = (result.ENERGY.Current == null) ? 0 : result.ENERGY.Current
missingChild = callChildParseByTypeId("POWER1", [[name:"current", value:"$r A"]], missingChild)
}
if (result.ENERGY.containsKey("ApparentPower")) {
logging("apparentPower: $result.ENERGY.ApparentPower VA",99)
missingChild = callChildParseByTypeId("POWER1", [[name:"apparentPower", value:"$result.ENERGY.ApparentPower VA"]], missingChild)
}
if (result.ENERGY.containsKey("ReactivePower")) {
logging("reactivePower: $result.ENERGY.ReactivePower VAr",99)
missingChild = callChildParseByTypeId("POWER1", [[name:"reactivePower", value:"$result.ENERGY.ReactivePower VAr"]], missingChild)
}
if (result.ENERGY.containsKey("Factor")) {
logging("powerFactor: $result.ENERGY.Factor",99)
missingChild = callChildParseByTypeId("POWER1", [[name:"powerFactor", value:"$result.ENERGY.Factor"]], missingChild)
}
if (result.ENERGY.containsKey("Voltage")) {
logging("Voltage: $result.ENERGY.Voltage V",99)
def r = (result.ENERGY.Voltage == null) ? 0 : result.ENERGY.Voltage
missingChild = callChildParseByTypeId("POWER1", [[name:"voltageWithUnit", value:"$r V"]], missingChild)
missingChild = callChildParseByTypeId("POWER1", [[name:"voltage", value: r, unit: "V"]], missingChild)
}
if (result.ENERGY.containsKey("Power")) {
logging("Power: $result.ENERGY.Power W",99)
def r = (result.ENERGY.Power == null) ? 0 : result.ENERGY.Power
missingChild = callChildParseByTypeId("POWER1", [[name:"powerWithUnit", value:"$r W"]], missingChild)
missingChild = callChildParseByTypeId("POWER1", [[name:"power", value: r, unit: "W"]], missingChild)
}
}
// END: getTasmotaNewParserForEnergyMonitor()
// BEGIN:getTasmotaNewParserForSensors()
for ( r in result ) {
if((r.key == 'StatusSNS' || r.key == 'SENSOR') && r.value instanceof Map) {
result << r
}
}
for ( r in result ) {
if(r.value instanceof Map && (r.value.containsKey("Temperature") ||
r.value.containsKey("ProbeTemperature") ||
r.value.containsKey("Humidity") || r.value.containsKey("Pressure") ||
r.value.containsKey("Distance") || r.value.containsKey("Illuminance") ||
r.value.containsKey("Gas") || r.value.containsKey("DewPoint"))) {
if (r.value.containsKey("Humidity")) {
logging("Humidity: RH $r.value.Humidity%", 99)
missingChild = callChildParseByTypeId(r.key, [[name: "humidity", value: r.value.Humidity, unit: "%"]], missingChild)
}
if (r.value.containsKey("Temperature")) {
logging("Temperature: $r.value.Temperature", 99)
String c = String.valueOf((char)(Integer.parseInt("00B0", 16)));
missingChild = callChildParseByTypeId(r.key, [[name: "temperature", value: r.value.Temperature, unit: "$c${location.temperatureScale}"]], missingChild)
} else if (r.value.containsKey("ProbeTemperature")) {
logging("ProbeTemperature: $r.value.ProbeTemperature", 99)
String c = String.valueOf((char)(Integer.parseInt("00B0", 16)));
missingChild = callChildParseByTypeId(r.key, [[name: "temperature", value: r.value.ProbeTemperature, unit: "$c${location.temperatureScale}"]], missingChild)
}
if (r.value.containsKey("DewPoint")) {
logging("DewPoint: $r.value.DewPoint", 99)
String c = String.valueOf((char)(Integer.parseInt("00B0", 16)));
missingChild = callChildParseByTypeId(r.key, [[name: "dewPoint", value: r.value.DewPoint, unit: "$c${location.temperatureScale}"]], missingChild)
}
if (r.value.containsKey("Pressure")) {
logging("Pressure: $r.value.Pressure", 99)
String pressureUnit = "mbar"
missingChild = callChildParseByTypeId(r.key, [[name: "pressure", value: r.value.Pressure, unit: pressureUnit]], missingChild)
}
if (r.value.containsKey("Gas")) {
logging("Pressure: $r.value.Gas", 99)
String gasUnit = "ohm"
missingChild = callChildParseByTypeId(r.key, [[name: "gas", value: r.value.Gas, unit: gasUnit]], missingChild)
}
if (r.value.containsKey("Distance")) {
logging("Distance: $r.value.Distance cm", 99)
def realDistance = Math.round((r.value.Distance as Double) * 100) / 100
missingChild = callChildParseByTypeId(r.key, [[name: "distance", value: String.format("%.2f cm", realDistance), unit: "cm"]], missingChild)
}
if (r.value.containsKey("Illuminance")) {
logging("Illuminance: $r.value.Illuminance lux", 99)
def realIlluminance = Math.round(r.value.Illuminance as Double)
missingChild = callChildParseByTypeId(r.key, [[name: "illuminance", value: realIlluminance, unit: "lux"]], missingChild)
}
}
}
// END: getTasmotaNewParserForSensors()
// BEGIN:getTasmotaNewParserForWifi()
if (result.containsKey("Wifi")) {
if (log99 == true && result.Wifi.containsKey("AP")) {
logging("AP: $result.Wifi.AP",99)
}
if (log99 == true && result.Wifi.containsKey("BSSId")) {
logging("BSSId: $result.Wifi.BSSId",99)
}
if (log99 == true && result.Wifi.containsKey("Channel")) {
logging("Channel: $result.Wifi.Channel",99)
}
if (result.Wifi.containsKey("RSSI")) {
logging("RSSI: $result.Wifi.RSSI",99)
String quality = "${result.Wifi.RSSI}%"
if(device.currentValue('wifiSignal') != quality) sendEvent(name: "wifiSignal", value: quality, isStateChange: false)
}
if (log99 == true && result.Wifi.containsKey("SSId")) {
logging("SSId: $result.Wifi.SSId",99)
}
}
// END: getTasmotaNewParserForWifi()
// BEGIN:getTasmotaNewParserForShutter()
if (result.containsKey("Shutter1")) {
logging("parser: Shutter1: $result.Shutter1", 1)
missingChild = callChildParseByTypeId("SHUTTER", [[name:"shutter", value:result.Shutter1.clone()]], missingChild)
}
// END: getTasmotaNewParserForShutter()
tasmota_updatePresence("present")
return missingChild
}
void updateNeededSettings() {
// BEGIN:getUpdateNeededSettingsTasmotaHeader()
Map currentProperties = state.currentProperties ?: [:]
state.settings = settings
String isUpdateNeeded = "NO"
if(runReset != null && runReset == 'RESET') {
for ( e in state.settings ) {
logging("Deleting '${e.key}' with value = ${e.value} from Settings", 50)
device.clearSetting("${e.key}")
device.removeSetting("${e.key}")
state?.settings?.remove("${e.key}")
}
}
tasmota_prepareDNI()
// END: getUpdateNeededSettingsTasmotaHeader()
if(deviceConfig == null) deviceConfig = "01generic-device"
Map deviceConfigMap = getDeviceConfiguration(deviceConfig)
String originalDeviceTemplateInput = deviceTemplateInput
String deviceTemplateInput = deviceConfigMap?.template
if(deviceTemplateInput == null) deviceTemplateInput = originalDeviceTemplateInput
if(deviceTemplateInput == "") deviceTemplateInput = null
String originalModuleNumber = moduleNumber
String moduleNumber = deviceConfigMap?.module
if(moduleNumber == null) moduleNumber = originalModuleNumber
if(moduleNumber == "") moduleNumber = null
if(deviceTemplateInput != null && moduleNumber == null) moduleNumber = 0
logging("updateNeededSettings: deviceConfigMap=$deviceConfigMap, deviceTemplateInput=$deviceTemplateInput, moduleNumber=$moduleNumber", 1)
// BEGIN:getUpdateNeededSettingsTasmotaDynamicModuleCommand()
tasmota_getAction(tasmota_getCommandString("Module", null))
tasmota_getAction(tasmota_getCommandString("Template", null))
boolean disableModuleSelectionSetting = disableModuleSelection
if(disableModuleSelectionSetting == null) disableModuleSelectionSetting = false
Integer moduleNumberUsed = null
if(moduleNumber == null || moduleNumber == '-1') {
moduleNumberUsed = -1
} else {
moduleNumberUsed = moduleNumber.toInteger()
}
boolean useDefaultTemplate = false
String defaultDeviceTemplate = ''
if(deviceTemplateInput != null && deviceTemplateInput == "0") {
useDefaultTemplate = true
defaultDeviceTemplate = ''
}
if(deviceTemplateInput == null || deviceTemplateInput == "") {
useDefaultTemplate = true
defaultDeviceTemplate = ''
}
if(deviceTemplateInput != null) deviceTemplateInput = deviceTemplateInput.replaceAll(' ','')
logging("disableModuleSelectionSetting=$disableModuleSelectionSetting, deviceTemplateInput=$deviceTemplateInput, moduleNumberUsed=$moduleNumberUsed, moduleNumber=$moduleNumber", 1)
if(disableModuleSelectionSetting == false && ((deviceTemplateInput != null && deviceTemplateInput != "") ||
(useDefaultTemplate && defaultDeviceTemplate != ""))) {
def usedDeviceTemplate = defaultDeviceTemplate
if(useDefaultTemplate == false && deviceTemplateInput != null && deviceTemplateInput != "") {
usedDeviceTemplate = deviceTemplateInput
}
logging("Setting the Template (${usedDeviceTemplate}) soon...", 100)
logging("templateData = ${device.currentValue('templateData')}", 10)
if(usedDeviceTemplate != '') moduleNumberUsed = 0
if(usedDeviceTemplate != null && device.currentValue('templateData') != usedDeviceTemplate) {
logging("The template is currently NOT set to '${usedDeviceTemplate}', it is set to '${device.currentValue('templateData')}'", 100)
tasmota_getAction(tasmota_getCommandString("Template", usedDeviceTemplate))
} else if (device.currentValue('module') == null){
tasmota_getAction(tasmota_getCommandString("Template", null))
}else if (usedDeviceTemplate != null) {
logging("The template is set to '${usedDeviceTemplate}' already!", 100)
}
} else {
logging("Can't set the Template...", 10)
logging(device.currentValue('templateData'), 10)
}
if(disableModuleSelectionSetting == false && moduleNumberUsed != null && moduleNumberUsed >= 0) {
logging("Setting the Module (${moduleNumberUsed}) soon...", 100)
logging("device.currentValue('module'): '${device.currentValue('module')}'", 10)
if(moduleNumberUsed != null && (device.currentValue('module') == null || !(device.currentValue('module').startsWith("[${moduleNumberUsed}:") || device.currentValue('module') == '0'))) {
logging("Currently not using module ${moduleNumberUsed}, using ${device.currentValue('module')}", 100)
tasmota_getAction(tasmota_getCommandString("Module", "${moduleNumberUsed}"))
} else if (moduleNumberUsed != null && device.currentValue('module') != null){
logging("This starts with [${moduleNumberUsed} ${device.currentValue('module')}",10)
} else if (device.currentValue('module') == null){
tasmota_getAction(tasmota_getCommandString("Module", null))
} else {
logging("Module is set to '${device.currentValue('module')}', and it's set to be null, report this to the creator of this driver!",10)
}
} else {
logging("Setting the Module has been disabled!", 10)
}
// END: getUpdateNeededSettingsTasmotaDynamicModuleCommand()
//logging("After getUpdateNeededSettingsTasmotaDynamicModuleCommand", 0)
installCommands = deviceConfigMap?.installCommands
if(installCommands == null || installCommands == '') installCommands = []
//logging("Got to just before tasmota_runInstallCommands", 0)
tasmota_runInstallCommands(installCommands)
// BEGIN:getUpdateNeededSettingsTasmotaFooter()
tasmota_getAction(tasmota_getCommandString("TelePeriod", "${tasmota_getTelePeriodValue()}"))
tasmota_getAction(tasmota_getCommandString("SetOption113", "1"))
tasmota_getAction(tasmota_getCommandString("Emulation", "2"))
tasmota_getAction(tasmota_getCommandString("HttpHookHost", device.hub.getDataValue("localIP")))
tasmota_getAction(tasmota_getCommandString("HubitatHost", device.hub.getDataValue("localIP")))
logging("HubitatPort: ${device.hub.getDataValue("localSrvPortTCP")}", 1)
tasmota_getAction(tasmota_getCommandString("HttpHookPort", device.hub.getDataValue("localSrvPortTCP")))
tasmota_getAction(tasmota_getCommandString("HubitatPort", device.hub.getDataValue("localSrvPortTCP")))
tasmota_getAction(tasmota_getCommandString("FriendlyName1", device.displayName.take(32)))
tasmota_getAction(tasmota_getCommandString("SetOption34", "20"))
int tzoffset = getLocation().timeZone.getOffset(now()) / 3600000
String tzoffsetWithSign = tzoffset < 0 ? "${tzoffset}" : "+${tzoffset}"
logging("Setting timezone to $tzoffsetWithSign", 10)
tasmota_getAction(tasmota_getCommandString("Timezone", tzoffsetWithSign))
logging("Scheduling tasmota_refreshChildren...", 1)
runIn(30, "tasmota_refreshChildren")
runIn(60, "tasmota_refreshChildrenAgain")
logging("Done scheduling tasmota_refreshChildren...", 1)
if(override == true) {
tasmota_sync(ipAddress)
}
sendEvent(name:"needUpdate", value: isUpdateNeeded, displayed:false, isStateChange: false)
// END: getUpdateNeededSettingsTasmotaFooter()
}
/** Calls TO Child devices */
boolean callChildParseByTypeId(String deviceTypeId, List