#!/usr/bin/env groovy import groovy.json.JsonSlurper CliBuilder cli = new CliBuilder(usage:'mqtt2thins.groovy') cli.with { h ('help') p longOpt:'print', 'Attribute listen', args:0, optionalArg:false n longOpt:'name', 'Name', args:1 } def opt = cli.parse(args) def normalize = { String text -> def newtext = text.toLowerCase().replaceAll(/[ #]+/, '-') return newtext.replaceAll('ü', 'ue') .replaceAll('ä', 'ae') .replaceAll('ö', 'oe') } def mosquitto_json = System.in.text def jsonSlurper = new JsonSlurper() def json = jsonSlurper.parseText(mosquitto_json.toString()) def typeMap = [ 'numeric' : 'number', 'binary' : 'switch', 'enum' : 'string', 'brightness' : 'dimmer', 'color_temp' : 'dimmer' ] def processMQTTOptions = { Map holder, String topic, String key, String type, String openhabType -> def mqttOptions = [ stateTopic : "$topic/$key" ] def writable = holder['access']==7 || holder['access']==3 if(writable) mqttOptions['commandTopic'] = "$topic/set/$key" if ( openhabType == 'switch' ) { mqttOptions['on'] = holder.value_on!=null ? holder.value_on : 'ON' mqttOptions['off'] = holder.value_off!=null ? holder.value_off : 'OFF' if(holder.value_on instanceof Boolean) { mqttOptions.remove('on') mqttOptions.remove('off') mqttOptions.transformationPattern="MAP:ONOFF.map" } } if ( type == 'enum' ) { mqttOptions.allowedStates = holder.values?.join(",") } if ( type == 'numeric' ) { if ( holder.value_min != null ) mqttOptions['min']=holder.value_min if ( holder.value_max ) mqttOptions['max']=holder.value_max if ( holder.unit ) mqttOptions['unit']=holder.unit } def mqttOptionsText = mqttOptions.collect { it -> def optionValue = it.value if ( it.value instanceof CharSequence ) { optionValue = "\"${it.value}\"" } "${it.key} = $optionValue" }.join(", ") return mqttOptionsText } def processDevice = { Map device, boolean print=false -> def friendly_name = device.friendly_name def config = device.definition if(print) { } def channels = [] def topic = "zigbee2mqtt/$friendly_name" for(Map exposeConfig in config.exposes) { if(print) { println "===\n$exposeConfig\n===" } def type = exposeConfig['type'] def key = exposeConfig['name'] def openhabType = typeMap[type] ?: type def firstFeature if(exposeConfig.features && !exposeConfig.features.empty) { for(feature in exposeConfig.features) { type = feature.type def property = feature.property openhabType = typeMap[type] ?: type if(typeMap[property]) { openhabType = typeMap[property] } key = feature.property ?: feature.name if( exposeConfig.endpoint ) { key += '_' + exposeConfig.endpoint } def keyPadded = key.padRight(15) def mqttOptionsText = processMQTTOptions(feature, topic, key, type, openhabType) channels << """Type $openhabType : $keyPadded "$key" [ $mqttOptionsText ]""" if ( feature.property == "color_temp" ) { // homekit, ga needs this as numeric key += '_num' keyPadded = key.padRight(15) channels << """Type number : $keyPadded "$key" [ $mqttOptionsText ]""" } } } else { def keyPadded = key.padRight(15) def mqttOptionsText = processMQTTOptions(exposeConfig, topic, key, type, openhabType) channels << """Type $openhabType : $keyPadded "$key" [ $mqttOptionsText ]""" } } def pad = "".padLeft(10) def padChannel = "".padLeft(20) println """\ ${pad}Thing topic $friendly_name "$friendly_name" { ${pad} Channels:""" for(def channel in channels) { println padChannel + channel } println """\ ${pad}}""" } for(Map device in json) { def friendly_name = device.friendly_name if(opt.n && !friendly_name.contains(opt.n)) continue; processDevice(device, opt.p) }