{"version":3,"sources":["jsdelivr-header.js","/npm/homebridge-mqttthing@1.1.49/index.js"],"names":[],"mappings":"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","file":"/npm/homebridge-mqttthing@1.1.49/index.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/homebridge-mqttthing@1.1.49/index.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","// MQTT Thing Accessory plugin for Homebridge\n// A Homebridge plugin for serveral services, based on homebrige-mqtt-switch and homebridge-mqttlightbulb\n\n/* eslint-disable object-property-newline */\n/* eslint-disable no-plusplus */\n\n'use strict'; // eslint-disable-line\n\nvar os = require( \"os\" );\nvar packagedef = require( './package.json' );\nvar homebridgeLib = require( 'homebridge-lib' );\nconst { EveHomeKitTypes } = require( 'homebridge-lib/EveHomeKitTypes' );\nvar fakegatoHistory = require( 'fakegato-history' );\nvar fs = require( \"fs\" );\nvar path = require( \"path\" );\nvar mqttlib = require( './libs/mqttlib' );\nconst EventEmitter = require( 'events' );\n\nvar Service, Characteristic, Eve, HistoryService;\nvar homebridgePath;\n\nfunction makeThing( log, accessoryConfig, api ) {\n\n    // Create accessory information service\n    function makeAccessoryInformationService() {\n        var informationService = new Service.AccessoryInformation();\n\n        informationService.setCharacteristic( Characteristic.Manufacturer, accessoryConfig.manufacturer || \"mqttthing\" );\n        informationService.setCharacteristic( Characteristic.Model, accessoryConfig.model || accessoryConfig.type );\n        informationService.setCharacteristic( Characteristic.SerialNumber, accessoryConfig.serialNumber || ( os.hostname() + \"-\" + accessoryConfig.name ) );\n        informationService.setCharacteristic( Characteristic.FirmwareRevision, accessoryConfig.firmwareRevision || packagedef.version );\n\n        return informationService;\n    }\n\n    //\n    //  MQTT Wrappers\n    //\n\n    // Initialize MQTT client\n    let ctx = { log, config: accessoryConfig, homebridgePath };\n    try {\n        mqttlib.init( ctx );\n    } catch( ex ) {\n        log.error( 'MQTT initialisation failed: ' + ex );\n        return { getServices: () => [] };\n    }\n\n    // MQTT Subscribe\n    function mqttSubscribe( topic, property, handler ) {\n        mqttlib.subscribe( ctx, topic, property, handler );\n    }\n\n    // MQTT Publish\n    function mqttPublish( topic, property, message ) {\n        mqttlib.publish( ctx, topic, property, message );\n    }\n\n    // Delayed one-shot function call\n    let throttledCallTimers = {};\n    let throttledCall = function( func, identifier, timeout ) {\n        if( throttledCallTimers[ identifier ] ) {\n            clearTimeout( throttledCallTimers[ identifier ] );\n        }\n        throttledCallTimers[ identifier ] = setTimeout( function() {\n            throttledCallTimers[ identifier ] = null;\n            func();\n        }, timeout );\n    };\n\n    // Controllers\n    let controllers = [];\n\n    // Create services\n    function createServices() {\n\n        function configToServices( config ) {\n\n            // Adaptive lighting support...\n\n            // Our controller\n            let adaptiveLightingController = null;\n            let adaptiveLightingEmitter = new EventEmitter();\n\n            // Test whether adaptive lighting is active\n            let isAdaptiveLightingActive = () => adaptiveLightingController && adaptiveLightingController.isAdaptiveLightingActive();\n\n            // Disable adaptive lighting (when user sets hue/saturation explicitly)\n            let disableAdaptiveLighting = function( what ) {\n                if( isAdaptiveLightingActive() ) {\n                    log( `External control (${what}) disabling adaptive lighting` );\n                    adaptiveLightingController.disableAdaptiveLighting();\n                }\n            };\n\n            // Do we support adaptive lighting?\n            let supportAdaptiveLighting = function() {\n                return ( config.adaptiveLighting !== false ) && api.versionGreaterOrEqual && api.versionGreaterOrEqual( '1.3.0-beta.27' );\n            };\n\n            // Create adaptive lighting controller\n            let addAdaptiveLightingController = function( service ) {\n                if( adaptiveLightingController ) {\n                    log.error( 'Logic error: Duplicate call to addAdaptiveLightingController() - ignoring' );\n                    return;\n                }\n                log( 'Enabling adaptive lighting' );\n                adaptiveLightingController = new api.hap.AdaptiveLightingController( service, {\n                    controllerMode: api.hap.AdaptiveLightingControllerMode.AUTOMATIC\n                } );\n                controllers.push( adaptiveLightingController );\n            };\n\n            // Migrate old-style history options\n            if( config.hasOwnProperty( 'history' ) ) {\n                if( typeof config.history == 'object' ) {\n                    config.historyOptions = config.history;\n                    config.history = true;\n                } else {\n                    if( !config.hasOwnProperty( 'historyOptions' ) ) {\n                        config.historyOptions = {};\n                    }\n                }\n                // migrate negated options for config-ui-x defaults\n                if( !config.historyOptions.hasOwnProperty( 'noAutoTimer' ) ) {\n                    config.historyOptions.noAutoTimer = ( config.historyOptions.autoTimer === false );\n                }\n                if( !config.historyOptions.hasOwnProperty( 'noAutoRepeat' ) ) {\n                    config.historyOptions.noAutoRepeat = ( config.historyOptions.autoRepeat === false );\n                }\n            }\n\n            // History persistence path\n            function historyPersistencePath() {\n                let directory;\n                if( config.historyOptions && config.historyOptions.persistencePath ) {\n                    if( config.historyOptions.persistencePath[ 0 ] == '/' ) {\n                        // full path\n                        directory = config.historyOptions.persistencePath;\n                    } else {\n                        // assume relative to homebridge path\n                        directory = path.join( homebridgePath, config.historyOptions.persistencePath );\n                    }\n                } else {\n                    // no path configured - use homebridge path\n                    directory = homebridgePath;\n                }\n                return directory;\n            }\n\n            function historyCounterFile() {\n                const counterFile = path.join( historyPersistencePath(), os.hostname().split( \".\" )[ 0 ] + \"_\" + config.name + \"_cnt_persist.json\" );\n                return counterFile;\n            }\n\n            const c_mySetContext = { mqttthing: '---my-set-context--' };\n\n            // constructor for fakegato-history options\n            function HistoryOptions( isEventSensor = false ) {\n                // maximum size of stored data points\n                this.size = config.historyOptions.size || 4032;\n                // data will be stored in .homebridge or path specified with homebridge -U option\n                this.storage = 'fs';\n                if( config.historyOptions.persistencePath ) {\n                    this.path = historyPersistencePath();\n                }\n                if( config.historyOptions.noAutoTimer === true || config.historyOptions.mergeInterval ) {\n                    // disable averaging (and repeating) interval timer\n                    // if mergeInterval is used, then autoTimer has to be deactivated (inconsistencies possible)\n                    this.disableTimer = true;\n                }\n                // disable repetition (if no data was received in last interval)\n                if( config.historyOptions.noAutoRepeat === true ) {\n                    if( isEventSensor ) {\n                        // for 'motion' and 'door' type\n                        this.disableTimer = true;\n                    } else {\n                        // for 'weather', 'room' and 'energy' type\n                        this.disableRepeatLastData = true;\n                    }\n                }\n            }\n\n            // The states of our characteristics\n            var state = ctx.state = {};\n\n            // Internal event handling\n            var events = {};\n\n            function raiseEvent( property ) {\n                if( events.hasOwnProperty( property ) ) {\n                    events[ property ]();\n                }\n            }\n\n            function makeConfirmedPublisher( setTopic, getTopic, property, makeConfirmed ) {\n                return mqttlib.makeConfirmedPublisher( ctx, setTopic, getTopic, property, makeConfirmed );\n            }\n\n            //! Determine appropriate on/off value for Boolean property (not forced to string) for MQTT publishing.\n            //! Returns null if no offValue.\n            function getOnOffPubValue( value ) {\n                let mqttval;\n                if( config.onValue ) {\n                    // using onValue/offValue\n                    mqttval = value ? config.onValue : config.offValue;\n                } else if( config.integerValue ) {\n                    mqttval = value ? 1 : 0;\n                } else {\n                    mqttval = value ? true : false;\n                }\n                if( mqttval === undefined || mqttval === null ) {\n                    return null;\n                } else {\n                    return mqttval;\n                }\n            }\n\n            //! Test whether a value represents 'on'\n            function isRecvValueOn( mqttval ) {\n                let onval = getOnOffPubValue( true );\n                return mqttval === onval || mqttval == ( onval + '' );\n            }\n\n            //! Test whether a value represents 'off'\n            function isRecvValueOff( mqttval ) {\n\n                if( config.otherValueOff ) {\n                    if( !isRecvValueOn( mqttval ) ) {\n                        // it's not the on value and we consider any other value to be off\n                        return true;\n                    }\n                }\n\n                let offval = getOnOffPubValue( false );\n\n                if( offval === null ) {\n                    // there is no off value\n                    return false;\n                }\n\n                if( mqttval === offval || mqttval == ( offval + '' ) ) {\n                    // off value match - it's definitely off\n                    return true;\n                }\n\n                // not off\n                return false;\n            }\n\n            function getOnlineOfflinePubValue( value ) {\n                var pubVal = ( value ? config.onlineValue : config.offlineValue );\n                if( pubVal === undefined ) {\n                    pubVal = getOnOffPubValue( value );\n                }\n                return pubVal;\n            }\n\n            function isRecvValueOnline( mqttval ) {\n                let onval = getOnlineOfflinePubValue( true );\n                return mqttval === onval || mqttval == ( onval + '' );\n            }\n\n            function isRecvValueOffline( mqttval ) {\n                let offval = getOnlineOfflinePubValue( false );\n                return mqttval === offval || mqttval == ( offval + '' );\n            }\n\n            function mapValueForHomebridge( val, mapValueFunc ) {\n                if( mapValueFunc ) {\n                    return mapValueFunc( val );\n                } else {\n                    return val;\n                }\n            }\n\n            function isOffline() {\n                return state.online === false;\n            }\n\n            function handleGetStateCallback( callback, value ) {\n                if( isOffline() ) {\n                    callback( 'offline' );\n                } else {\n                    callback( null, value );\n                }\n            }\n\n            function isSet( val ) {\n                return val !== undefined && val !== null;\n            }\n\n            function isValid( charac, value ) {\n\n                // if validation is disabled, accept anything\n                if( config.validate === false ) {\n                    return true;\n                }\n\n                const format = charac.props.format;\n                if( format === 'int' || format === \"uint8\" || format == \"uint16\" || format == \"uint32\" ) {\n                    if( !Number.isInteger( value ) ) {\n                        log( `Ignoring invalid value [${value}] for ${charac.displayName} - not an integer` );\n                        return false;\n                    }\n                    if( isSet( charac.props.minValue ) && value < charac.props.minValue ) {\n                        log( `Ignoring invalid value [${value}] for ${charac.displayName} - below minimum (${charac.props.minValue})` );\n                        return false;\n                    }\n                    if( isSet( charac.props.maxValue ) && value > charac.props.maxValue ) {\n                        log( `Ignoring invalid value [${value}] for ${charac.displayName} - above maximum (${charac.props.maxValue})` );\n                        return false;\n                    }\n                } else if( format === 'float' ) {\n                    if( typeof value !== 'number' || isNaN( value ) ) {\n                        log( `Ignoring invalid value [${value}] for ${charac.displayName} - not a number` );\n                        return false;\n                    }\n                    if( isSet( charac.props.minValue ) && value < charac.props.minValue ) {\n                        log( `Ignoring invalid value [${value}] for ${charac.displayName} - below minimum (${charac.props.minValue})` );\n                        return false;\n                    }\n                    if( isSet( charac.props.maxValue ) && value > charac.props.maxValue ) {\n                        log( `Ignoring invalid value [${value}] for ${charac.displayName} - above maximum (${charac.props.maxValue})` );\n                        return false;\n                    }\n                } else if( format === 'bool' ) {\n                    if( value !== true && value !== false ) {\n                        log( `Ignoring invalid value [${value}] for ${charac.displayName} - not a Boolean` );\n                        return false;\n                    }\n                } else if( format === 'string' ) {\n                    if( typeof value !== 'string' ) {\n                        log( `Ignoring invalid value [${value}] for ${charac.displayName} - not a string` );\n                        return false;\n                    }\n                } else {\n                    log( `Unable to validate ${charac.displayName}, format [${charac.props.format}] - ${JSON.stringify( charac )}` );\n                }\n                return true;\n            }\n\n            function setCharacteristic( charac, value ) {\n                if( isValid( charac, value ) ) {\n                    charac.setValue( value, undefined, c_mySetContext );\n                }\n            }\n\n            function booleanCharacteristic( service, property, characteristic, setTopic, getTopic, initialValue, mapValueFunc, turnOffAfterms, resetStateAfterms, enableConfirmation ) {\n\n                var publish = makeConfirmedPublisher( setTopic, getTopic, property, enableConfirmation );\n\n                // auto-turn-off and reset-state timers\n                var autoOffTimer = null;\n                var autoResetStateTimer = null;\n\n                // default state\n                state[ property ] = ( initialValue ? true : false );\n\n                // set up characteristic\n                var charac = service.getCharacteristic( characteristic );\n                charac.on( 'get', function( callback ) {\n                    handleGetStateCallback( callback, state[ property ] );\n                } );\n                if( setTopic ) {\n                    charac.on( 'set', function( value, callback, context ) {\n                        if( context !== c_mySetContext ) {\n                            state[ property ] = value;\n                            publish( getOnOffPubValue( value ) );\n                        }\n                        callback();\n\n                        // optionally turn off after timeout\n                        if( value && turnOffAfterms ) {\n                            if( autoOffTimer ) {\n                                clearTimeout( autoOffTimer );\n                            }\n                            autoOffTimer = setTimeout( function() {\n                                autoOffTimer = null;\n\n                                state[ property ] = false;\n                                publish( getOnOffPubValue( false ) );\n                                setCharacteristic( charac, mapValueForHomebridge( false, mapValueFunc ) );\n\n                            }, turnOffAfterms );\n                        }\n                    } );\n                }\n                if( initialValue ) {\n                    setCharacteristic( charac, mapValueForHomebridge( initialValue, mapValueFunc ) );\n                }\n\n                // subscribe to get topic\n                if( getTopic ) {\n                    mqttSubscribe( getTopic, property, function( topic, message ) {\n                        // determine whether this is an on or off value\n                        let newState = false; // assume off\n                        if( isRecvValueOn( message ) ) {\n                            newState = true; // received on value so on\n                        } else if( !isRecvValueOff( message ) ) {\n                            // received value NOT acceptable as 'off' so ignore message\n                            return;\n                        }\n                        // if it changed, set characteristic\n                        if( state[ property ] != newState ) {\n                            state[ property ] = newState;\n                            setCharacteristic( charac, mapValueForHomebridge( newState, mapValueFunc ) );\n                        }\n                        // optionally reset state to OFF after a timeout\n                        if( newState && resetStateAfterms ) {\n                            if( autoResetStateTimer ) {\n                                clearTimeout( autoResetStateTimer );\n                            }\n                            autoResetStateTimer = setTimeout( function() {\n                                autoResetStateTimer = null;\n                                state[ property ] = false;\n                                setCharacteristic( charac, mapValueForHomebridge( false, mapValueFunc ) );\n                            }, resetStateAfterms );\n                        }\n                    } );\n                }\n            }\n\n            function booleanState( property, getTopic, initialValue, isOnFunc, isOffFunc ) {\n                // default state\n                state[ property ] = ( initialValue ? true : false );\n\n                // MQTT subscription\n                if( getTopic ) {\n                    mqttSubscribe( getTopic, property, function( topic, message ) {\n                        if( isOnFunc( message ) ) {\n                            state[ property ] = true;\n                        } else if( isOffFunc( message ) ) {\n                            state[ property ] = false;\n                        }\n                    } );\n                }\n            }\n\n            function state_Online() {\n                booleanState( 'online', config.topics.getOnline, true, isRecvValueOnline, isRecvValueOffline );\n            }\n\n            function integerCharacteristic( service, property, characteristic, setTopic, getTopic, options ) {\n\n                let initialValue = options && options.initialValue;\n                let minValue = options && options.minValue;\n                let maxValue = options && options.maxValue;\n\n                // default state\n                state[ property ] = initialValue || 0;\n\n                // set up characteristic\n                var charac = service.getCharacteristic( characteristic );\n\n                // min/max\n                if( Number.isInteger( minValue ) ) {\n                    charac.props.minValue = minValue;\n                }\n                if( Number.isInteger( maxValue ) ) {\n                    charac.props.maxValue = maxValue;\n                }\n\n                // get/set\n                charac.on( 'get', function( callback ) {\n                    handleGetStateCallback( callback, state[ property ] );\n                } );\n\n                let onSet = function( value, context ) {\n                    if( context !== c_mySetContext ) {\n                        state[ property ] = value;\n                        if( setTopic ) {\n                            mqttPublish( setTopic, property, value );\n                        }\n                    }\n                    if( options && options.onSet ) {\n                        options.onSet( value, context );\n                    }\n                };\n\n                if( setTopic || ( options && options.onSet ) ) {\n                    charac.on( 'set', function( value, callback, context ) {\n                        onSet( value, context );\n                        callback();\n                    } );\n                }\n                if( initialValue ) {\n                    setCharacteristic( charac, initialValue );\n                }\n\n                // subscribe to get topic\n                if( getTopic ) {\n                    mqttSubscribe( getTopic, property, function( topic, message ) {\n                        var newState = parseInt( message );\n                        if( state[ property ] != newState ) {\n                            if( options && options.onMqtt ) {\n                                options.onMqtt( newState );\n                            }\n                            // update state and characteristic\n                            state[ property ] = newState;\n                            setCharacteristic( charac, newState );\n                        }\n                    } );\n                }\n\n                return { onSet };\n            }\n\n            function addCharacteristic( service, property, characteristic, defaultValue, characteristicChanged, adaptiveEventName ) {\n\n                state[ property ] = defaultValue;\n\n                var charac = service.getCharacteristic( characteristic );\n\n                setCharacteristic( charac, defaultValue );\n\n                charac.on( 'get', function( callback ) {\n                    let valReturned = state[ property ];\n                    if( !isValid( charac, valReturned ) ) {\n                        valReturned = defaultValue;\n                    }\n                    handleGetStateCallback( callback, valReturned );\n                } );\n\n                if( characteristicChanged ) {\n                    charac.on( 'set', function( value, callback, context ) {\n                        if( context !== c_mySetContext ) {\n                            state[ property ] = value;\n                            characteristicChanged();\n                        }\n                        callback();\n                    } );\n\n                    if( adaptiveEventName ) {\n                        adaptiveLightingEmitter.addListener( adaptiveEventName, ( value ) => {\n                            state[ property ] = value;\n                            characteristicChanged();\n                        } );\n                    }\n                }\n            }\n\n            function characteristics_HSVLight( service ) {\n\n                let lastpubmsg = '';\n\n                function publishNow() {\n                    var bri = state.bri;\n                    if( !config.topics.setOn && !state.on ) {\n                        bri = 0;\n                    }\n                    var msg = state.hue + ',' + state.sat + ',' + bri;\n                    if( msg != lastpubmsg ) {\n                        mqttPublish( config.topics.setHSV, 'HSV', msg );\n                        lastpubmsg = msg;\n                    }\n                }\n\n                function publish() {\n                    throttledCall( publishNow, 'hsv_publish', 20 );\n                }\n\n                if( config.topics.setOn ) {\n                    characteristic_On( service );\n                } else {\n                    addCharacteristic( service, 'on', Characteristic.On, false, function() {\n                        if( state.on && state.bri == 0 ) {\n                            state.bri = 100;\n                        }\n                        publish();\n                    } );\n                }\n                addCharacteristic( service, 'hue', Characteristic.Hue, 0, publish, 'hue' );\n                addCharacteristic( service, 'sat', Characteristic.Saturation, 0, publish, 'saturation' );\n                addCharacteristic( service, 'bri', Characteristic.Brightness, 100, function() {\n                    if( state.bri > 0 && !state.on ) {\n                        state.on = true;\n                    }\n                    publish();\n                } );\n\n                if( config.topics.getHSV ) {\n                    mqttSubscribe( config.topics.getHSV, 'HSV', function( topic, message ) {\n                        var comps = ( '' + message ).split( ',' );\n                        if( comps.length == 3 ) {\n\n                            var hue = parseInt( comps[ 0 ] );\n                            var sat = parseInt( comps[ 1 ] );\n                            var bri = parseInt( comps[ 2 ] );\n\n                            if( !config.topics.setOn ) {\n                                var on = bri > 0 ? 1 : 0;\n\n                                if( on != state.on ) {\n                                    state.on = on;\n                                    setCharacteristic( service.getCharacteristic( Characteristic.On ), on );\n                                }\n                            }\n\n                            if( hue != state.hue ) {\n                                disableAdaptiveLighting( 'HSV hue' );\n\n                                state.hue = hue;\n                                //log( 'hue ' + hue );\n                                setCharacteristic( service.getCharacteristic( Characteristic.Hue ), hue );\n                            }\n\n                            if( sat != state.sat ) {\n                                disableAdaptiveLighting( 'HSV saturation' );\n\n                                state.sat = sat;\n                                //log( 'sat ' + sat );\n                                setCharacteristic( service.getCharacteristic( Characteristic.Saturation ), sat );\n                            }\n\n                            if( bri != state.bri ) {\n                                state.bri = bri;\n                                //log( 'bri ' + bri );\n                                setCharacteristic( service.getCharacteristic( Characteristic.Brightness ), bri );\n                            }\n                        }\n                    } );\n                }\n\n                if( supportAdaptiveLighting() ) {\n                    characteristic_ColorTemperature_Internal( service );\n                }\n            }\n\n            /*\n             * HSV to RGB conversion from https://stackoverflow.com/questions/17242144/javascript-convert-hsb-hsv-color-to-rgb-accurately\n             * accepts parameters\n             * h  Object = {h:x, s:y, v:z}\n             * OR\n             * h, s, v\n             */\n            function HSVtoRGB( h, s, v ) {\n                var r, g, b, i, f, p, q, t;\n                if( arguments.length === 1 ) {\n                    s = h.s, v = h.v, h = h.h;\n                }\n                i = Math.floor( h * 6 );\n                f = h * 6 - i;\n                p = v * ( 1 - s );\n                q = v * ( 1 - f * s );\n                t = v * ( 1 - ( 1 - f ) * s );\n                switch( i % 6 ) {\n                    case 0: r = v, g = t, b = p; break;\n                    case 1: r = q, g = v, b = p; break;\n                    case 2: r = p, g = v, b = t; break;\n                    case 3: r = p, g = q, b = v; break;\n                    case 4: r = t, g = p, b = v; break;\n                    case 5: r = v, g = p, b = q; break;\n                }\n                return {\n                    r: Math.round( r * 255 ),\n                    g: Math.round( g * 255 ),\n                    b: Math.round( b * 255 )\n                };\n            }\n\n            function ScaledHSVtoRGB( h, s, v ) {\n                return HSVtoRGB( h / 360, s / 100, v / 100 );\n            }\n\n            /* accepts parameters\n             * r  Object = {r:x, g:y, b:z}\n             * OR\n             * r, g, b\n             */\n            function RGBtoHSV( r, g, b ) {\n                if( arguments.length === 1 ) {\n                    g = r.g, b = r.b, r = r.r;\n                }\n                var max = Math.max( r, g, b ), min = Math.min( r, g, b ),\n                    d = max - min,\n                    h,\n                    s = ( max === 0 ? 0 : d / max ),\n                    v = max / 255;\n\n                switch( max ) {\n                    case min: h = 0; break;\n                    case r: h = ( g - b ) + d * ( g < b ? 6 : 0 ); h /= 6 * d; break;\n                    case g: h = ( b - r ) + d * 2; h /= 6 * d; break;\n                    case b: h = ( r - g ) + d * 4; h /= 6 * d; break;\n                }\n\n                return {\n                    h: h,\n                    s: s,\n                    v: v\n                };\n            }\n\n            function RGBtoScaledHSV( r, g, b ) {\n                var hsv = RGBtoHSV( r, g, b );\n                return {\n                    h: hsv.h * 360,\n                    s: hsv.s * 100,\n                    v: hsv.v * 100\n                };\n            }\n\n            // byte to 2-characters of hex\n            function toHex( num ) {\n                var s = '0' + num.toString( 16 );\n                return s.substr( s.length - 2 );\n            }\n\n            function decodeRGBCommaSeparatedString( rgb ) {\n                if( rgb ) {\n                    var comps = ( '' + rgb ).split( ',' );\n                    if( comps.length == 3 ) {\n                        return { r: comps[ 0 ], g: comps[ 1 ], b: comps[ 2 ] };\n                    }\n                }\n            }\n\n            /*function calcWhiteFactor1( rgbin, white ) {\n                // scale rgb value to full brightness as comparing colours\n                let compmax = Math.max( rgbin.r, rgbin.g, rgbin.b );\n                if( compmax < 1 ) {\n                    return 0;\n                }\n                let rgbsc = 255 / compmax;\n                let rgb = {r: rgbin.r * rgbsc, g: rgbin.g * rgbsc, b: rgbin.b * rgbsc};\n                // calculate factors\n                var rf = 1, gf = 1, bf = 1;\n                if( white.r < 255 ) {\n                    rf = ( 255 - rgb.r ) / ( 255 - white.r ) / rgbsc;\n                }\n                if( white.g < 255 ) {\n                    gf = ( 255 - rgb.g ) / ( 255 - white.g ) / rgbsc;\n                }\n                if( white.b < 255 ) {\n                    bf = ( 255 - rgb.b ) / ( 255 - white.b ) / rgbsc;\n                }\n        \n                return Math.min( Math.max( 0, Math.min( rf, gf, bf ) ), 1 );\n            }*/\n\n            function calcWhiteFactor2( rgbin, white ) {\n                // scale rgb value to full brightness as comparing colours\n                let compmax = Math.max( rgbin.r, rgbin.g, rgbin.b );\n                if( compmax < 1 ) {\n                    return 0;\n                }\n                let rgbsc = 255 / compmax;\n                let rgb = { r: rgbin.r * rgbsc, g: rgbin.g * rgbsc, b: rgbin.b * rgbsc };\n                // calculate factors\n                let wmin = Math.min( white.r, white.g, white.b );\n                let cmin = Math.min( rgb.r, rgb.g, rgb.b );\n                var rf = 1, gf = 1, bf = 1;\n                if( white.r > wmin ) {\n                    rf = ( rgb.r - cmin ) / ( white.r - wmin ) / rgbsc;\n                }\n                if( white.g > wmin ) {\n                    gf = ( rgb.g - cmin ) / ( white.g - wmin ) / rgbsc;\n                }\n                if( white.b > wmin ) {\n                    bf = ( rgb.b - cmin ) / ( white.b - wmin ) / rgbsc;\n                }\n\n                return Math.min( Math.max( 0, Math.min( rf, gf, bf ) ), 1 );\n            }\n\n            function calcWhiteFactor( rgb, white ) {\n                let rf = 1, gf = 1, bf = 1;\n                if( white.r > 0 ) {\n                    rf = rgb.r / white.r;\n                }\n                if( white.g > 0 ) {\n                    gf = rgb.g / white.g;\n                }\n                if( white.b > 0 ) {\n                    bf = rgb.b / white.b;\n                }\n                return Math.min( Math.max( 0, Math.min( rf, gf, bf, calcWhiteFactor2( rgb, white ) ) ), 1 );\n            }\n\n            function characteristics_RGBLight( service ) {\n\n                var warmWhiteRGB, coldWhiteRGB;\n\n                state.red = 0;\n                state.green = 0;\n                state.blue = 0;\n                state.white = 0;\n                state.warmWhite = 0;\n                state.coldWhite = 0;\n\n                var setTopic, getTopic, numComponents, property;\n                var wwcwComps = false;\n                var whiteComp = false;\n                var whiteSep = false;\n                if( config.topics.setRGBWW ) {\n                    setTopic = config.topics.setRGBWW;\n                    getTopic = config.topics.getRGBWW;\n                    property = 'RGBWW';\n                    wwcwComps = true;\n                    numComponents = 5;\n                    warmWhiteRGB = decodeRGBCommaSeparatedString( config.warmWhite ) || { r: 255, g: 158, b: 61 };\n                    coldWhiteRGB = decodeRGBCommaSeparatedString( config.coldWhite ) || { r: 204, g: 219, b: 255 };\n                } else if( config.topics.setRGBW ) {\n                    setTopic = config.topics.setRGBW;\n                    getTopic = config.topics.getRGBW;\n                    property = 'RGBW';\n                    whiteComp = true;\n                    numComponents = 4;\n                } else {\n                    setTopic = config.topics.setRGB;\n                    getTopic = config.topics.getRGB;\n                    property = 'RGB';\n                    if( config.topics.setWhite ) {\n                        whiteSep = true;\n                    }\n                    numComponents = 3;\n                }\n\n                var hexPrefix = null;\n                if( config.hexPrefix ) {\n                    hexPrefix = config.hexPrefix;\n                } else if( config.hex ) {\n                    hexPrefix = '';\n                }\n\n                let lastpubmsg = '';\n\n                function publishNow() {\n                    var bri = state.bri;\n                    if( !config.topics.setOn && !state.on ) {\n                        bri = 0;\n                    }\n                    var rgb = ScaledHSVtoRGB( state.hue, state.sat, bri );\n                    let orig_rgb = { ww: 0, cw: 0, ...rgb };\n\n                    if( wwcwComps ) {\n                        //console.log( rgb );\n                        // calculate warm-white and cold-white factors (0-1 indicating proportion of warm/cold white in colour)\n                        let warmFactor = calcWhiteFactor( rgb, warmWhiteRGB );\n                        let coldFactor = calcWhiteFactor( rgb, coldWhiteRGB );\n                        //console.log( \"wf: \" + warmFactor );\n                        //console.log( \"cf: \" + coldFactor );\n                        // sum must be below 1\n                        let whiteFactor = warmFactor + coldFactor;\n                        if( whiteFactor > 1 ) {\n                            warmFactor = warmFactor / whiteFactor;\n                            coldFactor = coldFactor / whiteFactor;\n                            whiteFactor = 1;\n                        }\n                        // manipulate RGB values\n                        rgb.ww = Math.floor( warmFactor * 255 );\n                        rgb.cw = Math.floor( coldFactor * 255 );\n                        //console.log( \"ww: \" + rgb.ww );\n                        //console.log( \"cw: \" + rgb.cw );\n                        /*rgb.r = Math.floor( rgb.r * ( 1 - whiteFactor ) );\n                        rgb.g = Math.floor( rgb.g * ( 1 - whiteFactor ) );\n                        rgb.b = Math.floor( rgb.b * ( 1 - whiteFactor ) );*/\n                        rgb.r = Math.max( 0, Math.floor( rgb.r - warmFactor * warmWhiteRGB.r - coldFactor * coldWhiteRGB.r ) );\n                        rgb.g = Math.max( 0, Math.floor( rgb.g - warmFactor * warmWhiteRGB.g - coldFactor * coldWhiteRGB.g ) );\n                        rgb.b = Math.max( 0, Math.floor( rgb.b - warmFactor * warmWhiteRGB.b - coldFactor * coldWhiteRGB.b ) );\n                        // any remaining pure white level can be replaced with a mixture of cold and warm white\n                        let min = Math.min( rgb.r, rgb.g, rgb.b, 255 - rgb.ww, 255 - rgb.cw );\n                        rgb.ww += Math.floor( min / 2 );\n                        rgb.cw += Math.floor( min / 2 );\n                        rgb.r -= min;\n                        rgb.g -= min;\n                        rgb.b -= min;\n\n                        if( config.whiteMix === false || config.noWhiteMix === true ) {\n                            if( ( rgb.ww > 0 || rgb.cw > 0 ) && ( rgb.r > 0 || rgb.g > 0 || rgb.b > 0 ) ) {\n                                // mixing white and colours is not allowed on some devices\n                                let redThreshold = ( config.redThreshold === undefined ) ? 15 : config.redThreshold;\n                                let greenThreshold = ( config.greenThreshold === undefined ) ? 15 : config.greenThreshold;\n                                let blueThreshold = ( config.blueThreshold === undefined ) ? 15 : config.blueThreshold;\n                                if( rgb.r > redThreshold || rgb.g > greenThreshold || rgb.b > blueThreshold ) {\n                                    // colour\n                                    rgb = orig_rgb;\n                                } else {\n                                    // white\n                                    rgb.r = 0;\n                                    rgb.g = 0;\n                                    rgb.b = 0;\n                                }\n                            }\n                        }\n\n                        // store white state\n                        state.warmWhite = rgb.ww;\n                        state.coldWhite = rgb.cw;\n                    } else if( whiteSep || whiteComp ) {\n                        // remove common component from red, green and blue to white\n                        let min = Math.min( rgb.r, rgb.g, rgb.b );\n                        rgb.w = min;\n                        rgb.r -= min;\n                        rgb.g -= min;\n                        rgb.b -= min;\n\n                        state.white = rgb.w;\n                    }\n                    state.red = rgb.r;\n                    state.green = rgb.g;\n                    state.blue = rgb.b;\n\n                    var msg;\n                    if( hexPrefix == null ) {\n                        // comma-separated decimal\n                        msg = rgb.r + ',' + rgb.g + ',' + rgb.b;\n                        if( whiteComp ) {\n                            msg += ',' + rgb.w;\n                        } else if( wwcwComps ) {\n                            if( config.switchWhites ) {\n                                msg += ',' + rgb.cw + ',' + rgb.ww;\n                            } else {\n                                msg += ',' + rgb.ww + ',' + rgb.cw;\n                            }\n                        }\n                    } else {\n                        // hex\n                        msg = hexPrefix + toHex( rgb.r ) + toHex( rgb.g ) + toHex( rgb.b );\n                        if( whiteComp ) {\n                            msg += toHex( rgb.w );\n                        } else if( wwcwComps ) {\n                            if( config.switchWhites ) {\n                                msg += toHex( rgb.cw ) + toHex( rgb.ww );\n                            } else {\n                                msg += toHex( rgb.ww ) + toHex( rgb.cw );\n                            }\n                        }\n                    }\n                    if( msg != lastpubmsg ) {\n                        mqttPublish( setTopic, property, msg );\n                        lastpubmsg = msg;\n                    }\n\n                    if( whiteSep ) {\n                        mqttPublish( config.topics.setWhite, 'white', rgb.w );\n                    }\n                }\n\n                // hold off before publishing to ensure that all updated properties are collected first\n                function publish() {\n                    throttledCall( publishNow, 'rgb_publish', 20 );\n                }\n\n                if( config.topics.setOn ) {\n                    characteristic_On( service );\n                } else {\n                    addCharacteristic( service, 'on', Characteristic.On, false, function() {\n                        if( state.on && state.bri == 0 ) {\n                            state.bri = 100;\n                        }\n                        publish();\n                    } );\n                }\n                addCharacteristic( service, 'hue', Characteristic.Hue, 0, publish, 'hue' );\n                addCharacteristic( service, 'sat', Characteristic.Saturation, 0, publish, 'saturation' );\n                addCharacteristic( service, 'bri', Characteristic.Brightness, 100, function() {\n                    if( state.bri > 0 && !state.on ) {\n                        state.on = true;\n                    }\n\n                    publish();\n                } );\n\n                function updateColour( red, green, blue, white, warmWhite, coldWhite ) {\n\n                    // add warm white/cold white in\n                    if( wwcwComps ) {\n                        red += Math.floor( warmWhiteRGB.r * warmWhite / 255 ) + Math.floor( coldWhiteRGB.r * coldWhite / 255 );\n                        green += Math.floor( warmWhiteRGB.g * warmWhite / 255 ) + Math.floor( coldWhiteRGB.g * coldWhite / 255 );\n                        blue += Math.floor( warmWhiteRGB.b * warmWhite / 255 ) + Math.floor( coldWhiteRGB.b * coldWhite / 255 );\n                    }\n\n                    // add any white component to red, green and blue\n                    red = Math.min( red + white, 255 );\n                    green = Math.min( green + white, 255 );\n                    blue = Math.min( blue + white, 255 );\n\n                    var hsv = RGBtoScaledHSV( red, green, blue );\n                    var hue = Math.floor( hsv.h );\n                    var sat = Math.floor( hsv.s );\n                    var bri = Math.floor( hsv.v );\n\n                    if( !config.topics.setOn ) {\n                        var on = bri > 0 ? 1 : 0;\n\n                        if( on != state.on ) {\n                            state.on = on;\n                            //log( 'on ' + on );\n                            setCharacteristic( service.getCharacteristic( Characteristic.On ), on );\n                        }\n                    }\n\n                    if( hue != state.hue ) {\n                        disableAdaptiveLighting( 'calculated hue' );\n\n                        state.hue = hue;\n                        //log( 'hue ' + hue );\n                        setCharacteristic( service.getCharacteristic( Characteristic.Hue ), hue );\n                    }\n\n                    if( sat != state.sat ) {\n                        disableAdaptiveLighting( 'calculated saturation' );\n\n                        state.sat = sat;\n                        //log( 'sat ' + sat );\n                        setCharacteristic( service.getCharacteristic( Characteristic.Saturation ), sat );\n                    }\n\n                    if( bri != state.bri ) {\n                        state.bri = bri;\n                        //log( 'bri ' + bri );\n                        setCharacteristic( service.getCharacteristic( Characteristic.Brightness ), bri );\n                    }\n                }\n\n                if( getTopic ) {\n                    mqttSubscribe( getTopic, property, function( topic, message ) {\n                        var ok = false;\n                        var red, green, blue, white, warmWhite, coldWhite;\n                        if( hexPrefix == null ) {\n                            // comma-separated decimal\n                            var comps = ( '' + message ).split( ',' );\n                            if( comps.length == numComponents ) {\n                                red = parseInt( comps[ 0 ] );\n                                green = parseInt( comps[ 1 ] );\n                                blue = parseInt( comps[ 2 ] );\n                                if( whiteComp ) {\n                                    white = parseInt( comps[ 3 ] );\n                                } else if( wwcwComps ) {\n                                    warmWhite = parseInt( comps[ 3 ] );\n                                    coldWhite = parseInt( comps[ 4 ] );\n                                    if( config.switchWhites ) {\n                                        let temp = warmWhite;\n                                        warmWhite = coldWhite;\n                                        coldWhite = temp;\n                                    }\n                                }\n                                ok = true;\n                            }\n                        } else {\n                            // hex\n                            if( message.length == hexPrefix.length + 2 * numComponents ) {\n                                message = '' + message;\n                                if( message.substr( 0, hexPrefix.length ) == hexPrefix ) {\n                                    red = parseInt( message.substr( hexPrefix.length, 2 ), 16 );\n                                    green = parseInt( message.substr( hexPrefix.length + 2, 2 ), 16 );\n                                    blue = parseInt( message.substr( hexPrefix.length + 4, 2 ), 16 );\n                                    if( whiteComp ) {\n                                        white = parseInt( message.substr( hexPrefix.length + 6, 2 ), 16 );\n                                    } else if( wwcwComps ) {\n                                        warmWhite = parseInt( message.substr( hexPrefix.length + 6, 2 ), 16 );\n                                        coldWhite = parseInt( message.substr( hexPrefix.length + 8, 2 ), 16 );\n                                        if( config.switchWhites ) {\n                                            let temp = warmWhite;\n                                            warmWhite = coldWhite;\n                                            coldWhite = temp;\n                                        }\n                                    }\n                                    ok = true;\n                                }\n                            }\n                        }\n                        if( ok ) {\n                            state.red = red;\n                            state.green = green;\n                            state.blue = blue;\n                            if( whiteComp ) {\n                                state.white = white;\n                                updateColour( red, green, blue, white );\n                            } else if( wwcwComps ) {\n                                state.warmWhite = warmWhite;\n                                state.coldWhite = coldWhite;\n                                updateColour( red, green, blue, 0, warmWhite, coldWhite );\n                            } else if( whiteSep ) {\n                                updateColour( red, green, blue, state.white );\n                            } else {\n                                updateColour( red, green, blue, 0 );\n                            }\n                        }\n                    } );\n                }\n\n                if( whiteSep ) {\n                    mqttSubscribe( config.topics.getWhite, 'white', function( topic, message ) {\n                        state.white = parseInt( message );\n                        updateColour( state.red, state.green, state.blue, state.white );\n                    } );\n                }\n\n                if( supportAdaptiveLighting() ) {\n                    characteristic_ColorTemperature_Internal( service );\n                }\n            }\n\n            function characteristics_WhiteLight( service ) {\n                state.white = 0;\n                var hexPrefix = null;\n                if( config.hexPrefix ) {\n                    hexPrefix = config.hexPrefix;\n                } else if( config.hex ) {\n                    hexPrefix = '';\n                }\n\n                function publish() {\n                    var bri = state.bri;\n                    if( !state.on ) {\n                        bri = 0;\n                    }\n                    var white = Math.min( Math.ceil( bri * 2.55 ), 255 );\n                    var msg;\n                    if( hexPrefix == null ) {\n                        msg = white;\n                    } else {\n                        msg = hexPrefix + toHex( white );\n                    }\n                    mqttPublish( config.topics.setWhite, 'white', msg );\n                }\n\n                addCharacteristic( service, 'on', Characteristic.On, false, function() {\n                    if( state.on && state.bri == 0 ) {\n                        state.bri = 100;\n                    }\n                    publish();\n                } );\n\n                addCharacteristic( service, 'bri', Characteristic.Brightness, 100, function() {\n                    if( state.bri > 0 && !state.on ) {\n                        state.on = true;\n                    }\n\n                    publish();\n                } );\n\n                if( config.topics.getWhite ) {\n                    mqttSubscribe( config.topics.getWhite, 'white', function( topic, message ) {\n                        var ok = false;\n                        var white;\n                        if( hexPrefix == null ) {\n                            var comps = ( '' + message ).split( ',' );\n                            if( comps.length == 1 ) {\n                                white = parseInt( comps[ 0 ] );\n                                ok = true;\n                            }\n                        } else {\n                            // hex\n                            if( message.length == hexPrefix.length + 2 ) {\n                                message = '' + message;\n                                if( message.substr( 0, hexPrefix.length ) == hexPrefix ) {\n                                    white = parseInt( message.substr( hexPrefix.length, 2 ), 16 );\n                                    ok = true;\n                                }\n                            }\n                        }\n                        if( ok ) {\n                            let bri = Math.min( Math.floor( white / 2.55 ), 100 );\n                            let on = bri > 0 ? true : false;\n\n                            if( on != state.on ) {\n                                state.on = on;\n                                setCharacteristic( service.getCharacteristic( Characteristic.On ), on );\n                            }\n\n                            if( bri != state.bri ) {\n                                state.bri = bri;\n                                setCharacteristic( service.getCharacteristic( Characteristic.Brightness ), bri );\n                            }\n                        }\n                    } );\n                }\n            }\n\n            function floatCharacteristic( service, property, characteristic, setTopic, getTopic, options ) {\n\n                if( options === undefined ) {\n                    options = {};\n                } else if( typeof options === 'number' ) {\n                    options = { initialValue: options };\n                }\n                let initialValue = options.initialValue || 0;\n\n                // set up characteristic\n                var charac = service.getCharacteristic( characteristic );\n\n                if( options.minValue !== undefined ) {\n                    charac.props.minValue = options.minValue;\n                }\n\n                if( options.maxValue !== undefined ) {\n                    charac.props.maxValue = options.maxValue;\n                }\n\n                if( initialValue < charac.props.minValue ) {\n                    initialValue = charac.props.minValue;\n                }\n\n                if( initialValue > charac.props.maxValue ) {\n                    initialValue = charac.props.maxValue;\n                }\n\n                // default state\n                state[ property ] = initialValue;\n\n                // get/set\n                charac.on( 'get', function( callback ) {\n                    handleGetStateCallback( callback, state[ property ] );\n                } );\n                if( setTopic ) {\n                    charac.on( 'set', function( value, callback, context ) {\n                        if( context !== c_mySetContext ) {\n                            state[ property ] = value;\n                            mqttPublish( setTopic, property, value );\n                        }\n                        callback();\n                    } );\n                }\n                if( initialValue ) {\n                    setCharacteristic( charac, initialValue );\n                }\n\n                // subscribe to get topic\n                if( getTopic ) {\n                    mqttSubscribe( getTopic, property, function( topic, message ) {\n                        var newState = parseFloat( message );\n                        if( state[ property ] != newState ) {\n                            state[ property ] = newState;\n                            setCharacteristic( charac, newState );\n                        }\n                    } );\n                }\n            }\n\n            function stringCharacteristic( service, property, characteristic, setTopic, getTopic, initialValue ) {\n                // default state\n                state[ property ] = initialValue ? initialValue : '';\n\n                // set up characteristic\n                var charac = service.getCharacteristic( characteristic );\n                charac.on( 'get', function( callback ) {\n                    handleGetStateCallback( callback, state[ property ] );\n                } );\n                if( setTopic ) {\n                    charac.on( 'set', function( value, callback, context ) {\n                        if( context !== c_mySetContext ) {\n                            state[ property ] = value;\n                            mqttPublish( setTopic, property, value );\n                        }\n                        callback();\n                    } );\n                }\n\n                // subscribe to get topic\n                if( getTopic ) {\n                    mqttSubscribe( getTopic, property, function( topic, message ) {\n                        var newState = message.toString();\n                        if( state[ property ] !== newState ) {\n                            state[ property ] = newState;\n                            setCharacteristic( charac, newState );\n                        }\n                    } );\n                }\n            }\n\n            function multiCharacteristic( service, property, characteristic, setTopic, getTopic, values, initialValue, eventOnly ) {\n                // Values is an array of MQTT values indexed by <value of Homekit enumeration>.\n                // Build map of MQTT values to homekit values\n                var mqttToHomekit = {};\n                for( let i = 0; i < values.length; i++ ) {\n                    mqttToHomekit[ values[ i ] ] = i;\n                }\n\n                state[ property ] = initialValue;\n\n                var charac = service.getCharacteristic( characteristic );\n\n                // Homekit get\n                if( !eventOnly ) {\n                    charac.on( 'get', function( callback ) {\n                        handleGetStateCallback( callback, state[ property ] );\n                    } );\n                }\n\n                // Homekit set\n                if( setTopic ) {\n                    charac.on( 'set', function( value, callback, context ) {\n                        if( context !== c_mySetContext ) {\n\n                            if( typeof value === \"boolean\" ) {\n                                value = value ? 1 : 0;\n                            }\n\n                            state[ property ] = value;\n                            let mqttVal = values[ value ];\n                            if( mqttVal !== undefined ) {\n                                mqttPublish( setTopic, property, mqttVal );\n                            }\n                            raiseEvent( property );\n                        }\n                        callback();\n                    } );\n                }\n\n                if( initialValue ) {\n                    setCharacteristic( charac, initialValue );\n                }\n\n                // MQTT set (Homekit get)\n                if( getTopic ) {\n                    mqttSubscribe( getTopic, property, function( topic, message ) {\n                        let data = message?.toString() ?? '';\n                        let newState = mqttToHomekit[ data ];\n                        if( newState !== undefined && ( eventOnly || state[ property ] != newState ) ) {\n                            if( config.logMqtt ) {\n                                log( `Received ${data} - ${property} state is now ${newState}` );\n                            }\n                            state[ property ] = newState;\n                            setCharacteristic( charac, newState );\n                            raiseEvent( property );\n                        }\n                        if( newState === undefined && config.logMqtt ) {\n                            log( `Warning: ${property} received [${data}] which is not in configured values ${JSON.stringify( mqttToHomekit )}` );\n                        }\n                    } );\n                }\n            }\n\n            // Characteristic.On\n            function characteristic_On( service ) {\n                booleanCharacteristic( service, 'on', Characteristic.On, config.topics.setOn, config.topics.getOn, null, null, config.turnOffAfterms, config.resetStateAfterms, true );\n            }\n\n            // History for On (Eve-only)\n            function history_On( historySvc, service ) {\n                characteristic_LastActivation( historySvc, service );\n\n                // get characteristic to be logged\n                var charac = service.getCharacteristic( Characteristic.On );\n                // attach change callback for this characteristic\n                charac.on( 'change', function( obj ) {\n                    var logEntry = {\n                        time: Math.floor( Date.now() / 1000 ),  // seconds (UTC)\n                        status: ( obj.newValue ? 1 : 0 )  // fakegato-history logProperty 'status' for switch\n                    };\n                    historySvc.addEntry( logEntry );\n                    // update Eve's Characteristic.LastActivation\n                    state.lastActivation = logEntry.time - historySvc.getInitialTime();\n                    service.updateCharacteristic( Eve.Characteristics.LastActivation, state.lastActivation );\n                } );\n            }\n\n            // Characteristic.Brightness\n            function characteristic_Brightness( service ) {\n\n                if( config.topics.setOn ) {\n                    // separate On topic, so implement standard brightness characteristic\n                    integerCharacteristic( service, 'brightness', Characteristic.Brightness, config.topics.setBrightness, config.topics.getBrightness );\n                } else {\n                    // no separate On topic, so use Brightness 0 to indicate Off state...\n\n                    // subscription\n                    if( config.topics.getBrightness ) {\n                        mqttSubscribe( config.topics.getBrightness, 'brightness', function( topic, message ) {\n                            let newState = parseInt( message );\n                            let newOn = ( newState != 0 );\n                            if( state.brightness != newState || state.on != newOn ) {\n                                if( newOn ) {\n                                    state.brightness = newState;\n                                    setCharacteristic( service.getCharacteristic( Characteristic.Brightness ), newState );\n                                }\n                                state.on = newOn;\n                                setCharacteristic( service.getCharacteristic( Characteristic.On ), newState != 0 );\n                            }\n                        } );\n                    }\n\n                    // publishing (throttled)\n                    let publishNow = function() {\n                        let bri = state.brightness;\n                        if( !config.topics.setOn && !state.on ) {\n                            bri = 0;\n                        }\n                        mqttPublish( config.topics.setBrightness, 'brightness', bri );\n                    };\n\n                    let publish = () => throttledCall( publishNow, 'brightness_pub', 20 );\n\n                    // Brightness characteristic\n                    addCharacteristic( service, 'brightness', Characteristic.Brightness, 0, () => {\n                        if( state.brightness > 0 && !state.on ) {\n                            state.on = true;\n                        }\n                        publish();\n                    } );\n\n                    // On Characteristic\n                    addCharacteristic( service, 'on', Characteristic.On, false, function() {\n                        if( state.on && state.brightness == 0 ) {\n                            state.brightness = 100;\n                        }\n                        publish();\n                    } );\n                }\n            }\n\n            // Characteristic.Hue\n            function characteristic_Hue( service ) {\n                let char = integerCharacteristic( service, 'hue', Characteristic.Hue, config.topics.setHue, config.topics.getHue, {\n                    onMqtt: () => disableAdaptiveLighting( 'hue' )\n                } );\n                if( supportAdaptiveLighting() ) {\n                    adaptiveLightingEmitter.addListener( 'hue', ( value ) => char.onSet( value ) );\n                }\n            }\n\n            // Characteristic.Saturation\n            function characteristic_Saturation( service ) {\n                let char = integerCharacteristic( service, 'saturation', Characteristic.Saturation, config.topics.setSaturation, config.topics.getSaturation, {\n                    onMqtt: () => disableAdaptiveLighting( 'saturation' )\n                } );\n                if( supportAdaptiveLighting() ) {\n                    adaptiveLightingEmitter.addListener( 'saturation', ( value ) => char.onSet( value ) );\n                }\n            }\n\n            // Characteristic.ColorTemperature\n            function characteristic_ColorTemperature( service ) {\n                integerCharacteristic( service, 'colorTemperature', Characteristic.ColorTemperature, config.topics.setColorTemperature, config.topics.getColorTemperature, {\n                    initialValue: ( config.minColorTemperature || 140 ),\n                    minValue: config.minColorTemperature,\n                    maxValue: config.maxColorTemperature,\n                    onMqtt: () => disableAdaptiveLighting( 'colorTemperature' )\n                } );\n\n                if( supportAdaptiveLighting() ) {\n                    addAdaptiveLightingController( service );\n                }\n            }\n\n            // 'Internal' Characteristic.ColorTemperature for adaptive lighting implementation\n            function characteristic_ColorTemperature_Internal( service ) {\n                integerCharacteristic( service, 'colorTemperature', Characteristic.ColorTemperature, null, null, {\n                    initialValue: 140,\n                    onSet: ( value ) => {\n                        // update saturation and hue to match\n                        let calc = api.hap.ColorUtils.colorTemperatureToHueAndSaturation( value );\n                        service.getCharacteristic( Characteristic.Saturation ).updateValue( calc.saturation );\n                        service.getCharacteristic( Characteristic.Hue ).updateValue( calc.hue );\n                        adaptiveLightingEmitter.emit( 'saturation', calc.saturation );\n                        adaptiveLightingEmitter.emit( 'hue', calc.hue );\n                    }\n                } );\n\n                if( supportAdaptiveLighting() ) {\n                    addAdaptiveLightingController( service );\n                }\n            }\n\n            // Characteristic.OutletInUse\n            function characteristic_OutletInUse( service ) {\n                booleanCharacteristic( service, 'outletInUse', Characteristic.OutletInUse, null, config.topics.getInUse );\n            }\n\n            // Characteristic.Name\n            function characteristic_Name( service ) {\n                stringCharacteristic( service, 'name', Characteristic.Name, null, config.topics.getName, config.name );\n            }\n\n            // Characteristic.MotionDetected\n            function characteristic_MotionDetected( service ) {\n                booleanCharacteristic( service, 'motionDetected', Characteristic.MotionDetected, null, config.topics.getMotionDetected, null, null, null, config.turnOffAfterms );\n            }\n\n            // Add Eve.Characteristics.LastActivation for History\n            function characteristic_LastActivation( historySvc, service ) {\n                service.addOptionalCharacteristic( Eve.Characteristics.LastActivation ); // to avoid warnings\n                // get lastActivation time from history data (check 5s later to make sure the history is loaded)\n                setTimeout( function() {\n                    if( historySvc.lastEntry && historySvc.memorySize ) {\n                        let entry = historySvc.history[ historySvc.lastEntry % historySvc.memorySize ];\n                        if( entry && entry.hasOwnProperty( 'time' ) ) {\n                            let lastTime = entry.time - historySvc.getInitialTime();\n                            addCharacteristic( service, 'lastActivation', Eve.Characteristics.LastActivation, lastTime );\n                            log.debug( 'lastActivation time loaded' );\n                        }\n                    }\n                }, 5000 );\n            }\n\n            // History for MotionDetected\n            function history_MotionDetected( historySvc, service ) {\n                var historyMergeTimer = null;\n                characteristic_LastActivation( historySvc, service );\n\n                // get characteristic to be logged\n                var charac = service.getCharacteristic( Characteristic.MotionDetected );\n                // attach change callback for this characteristic\n                charac.on( 'change', function( obj ) {\n                    var logEntry = {\n                        time: Math.floor( Date.now() / 1000 ),  // seconds (UTC)\n                        status: ( obj.newValue ? 1 : 0 )  // fakegato-history logProperty 'status' for motion sensor\n                    };\n                    // update Eve's Characteristic.LastActivation\n                    state.lastActivation = logEntry.time - historySvc.getInitialTime();\n                    service.updateCharacteristic( Eve.Characteristics.LastActivation, state.lastActivation );\n\n                    let mergeInterval = config.historyOptions.mergeInterval * 60000 || 0;\n                    if( logEntry.status ) {\n                        if( historyMergeTimer ) {\n                            // reset timer -> discard off-event\n                            clearTimeout( historyMergeTimer );\n                            historyMergeTimer = null;\n                        }\n                        historySvc.addEntry( logEntry );\n                    } else {\n                        if( historyMergeTimer ) {\n                            // reset timer\n                            clearTimeout( historyMergeTimer );\n                        }\n                        if( mergeInterval > 0 ) {\n                            // log off-event later (with original time),\n                            // if there is no new on-event in the given time.\n                            historyMergeTimer = setTimeout( function() {\n                                historyMergeTimer = null;\n                                historySvc.addEntry( logEntry );\n                            }, mergeInterval );\n                        } else {\n                            historySvc.addEntry( logEntry );\n                        }\n                    }\n                } );\n            }\n\n            // Characteristic.StatusActive\n            function characteristic_StatusActive( service ) {\n                booleanCharacteristic( service, 'statusActive', Characteristic.StatusActive, null, config.topics.getStatusActive, true );\n            }\n\n            // Characteristic.StatusFault\n            function characteristic_StatusFault( service ) {\n                booleanCharacteristic( service, 'statusFault', Characteristic.StatusFault, null, config.topics.getStatusFault );\n            }\n\n            // Characteristic.StatusTampered\n            function characteristic_StatusTampered( service ) {\n                booleanCharacteristic( service, 'statusTampered', Characteristic.StatusTampered, null, config.topics.getStatusTampered );\n            }\n\n            // Characteristic.AltSensorState to help detecting triggered state with multiple sensors\n            function characteristic_AltSensorState( /*service*/ ) {\n                // additional MQTT subscription instead of set-callback due to correct averaging:\n                mqttSubscribe( config.topics.getAltSensorState, 'AltSensorState', function( topic, message ) {\n                    // determine whether this is an on or off value\n                    let newState = false; // assume off\n                    if( isRecvValueOn( message ) ) {\n                        newState = true; // received on value so on\n                    } else if( !isRecvValueOff( message ) ) {\n                        // received value NOT acceptable as 'off' so ignore message\n                        return;\n                    }\n\n                    // not a real property/state ??? - no property/propChangedHandler so disabling code below...\n                    //  TODO: check with Ferme de Pommerieux\n                    log.warn( `AltSensorState now ${newState ? 'on' : 'off'} - TODO: update state and set characteristic??` );\n\n                    /*\n                    // if changed, set\n                    if( state[ property ] != newState ) {\n                        state[ property ] = newState;\n                        propChangedHandler();\n                    }\n                    */\n                } );\n            }\n\n            // Characteristic.StatusLowBattery\n            function characteristic_StatusLowBattery( service ) {\n                booleanCharacteristic( service, 'statusLowBattery', Characteristic.StatusLowBattery, null, config.topics.getStatusLowBattery );\n            }\n\n            // Characteristic.OccupancyDetected\n            function characteristic_OccupancyDetected( service ) {\n                booleanCharacteristic( service, 'occupancyDetected', Characteristic.OccupancyDetected, null, config.topics.getOccupancyDetected, false, function( val ) {\n                    return val ? Characteristic.OccupancyDetected.OCCUPANCY_DETECTED : Characteristic.OccupancyDetected.OCCUPANCY_NOT_DETECTED;\n                } );\n            }\n\n            // Characteristic.CurrentAmbientLightLevel\n            function characteristic_CurrentAmbientLightLevel( service ) {\n                floatCharacteristic( service, 'currentAmbientLightLevel', Characteristic.CurrentAmbientLightLevel,\n                    null, config.topics.getCurrentAmbientLightLevel, 0.0001 );\n            }\n\n            // Characteristic.CurrentTemperature\n            function characteristic_CurrentTemperature( service ) {\n                floatCharacteristic( service, 'currentTemperature', Characteristic.CurrentTemperature,\n                    null, config.topics.getCurrentTemperature, 0 );\n\n                // configured temperature ranges\n                if( !tempRange( service, Characteristic.CurrentTemperature ) ) {\n                    // or (old behaviour) allow negative temperatures (down to -100)\n                    let characteristic = service.getCharacteristic( Characteristic.CurrentTemperature );\n                    characteristic.props.minValue = -100;\n                }\n            }\n\n            // History for CurrentTemperature (Eve-only)\n            function history_CurrentTemperature( historySvc ) {\n                if( config.topics.getCurrentTemperature ) {\n                    // additional MQTT subscription instead of set-callback due to correct averaging:\n                    mqttSubscribe( config.topics.getCurrentTemperature, 'currentTemperature', function( topic, message ) {\n                        var logEntry = {\n                            time: Math.floor( Date.now() / 1000 ),  // seconds (UTC)\n                            temp: parseFloat( message )  // fakegato-history logProperty 'temp' for temperature sensor\n                        };\n                        historySvc.addEntry( logEntry );\n                    } );\n                }\n            }\n\n            function tempRange( service, theCharacteristic ) {\n                let customRangeSet = false;\n                if( config.minTemperature !== undefined || config.maxTemperature !== undefined ) {\n                    customRangeSet = true;\n                    var characteristic = service.getCharacteristic( theCharacteristic );\n                    if( config.minTemperature !== undefined ) {\n                        characteristic.props.minValue = config.minTemperature;\n                    }\n                    if( config.maxTemperature !== undefined ) {\n                        characteristic.props.maxValue = config.maxTemperature;\n                    }\n                }\n                return customRangeSet;\n            }\n\n            // Characteristic.TargetTemperature\n            function characteristic_TargetTemperature( service ) {\n                floatCharacteristic( service, 'targetTemperature', Characteristic.TargetTemperature,\n                    config.topics.setTargetTemperature, config.topics.getTargetTemperature, 10 );\n\n                // custom min/max\n                tempRange( service, Characteristic.TargetTemperature );\n            }\n\n            // Characteristic.CoolingThresholdTemperature\n            function characteristic_CoolingThresholdTemperature( service ) {\n                floatCharacteristic( service, 'coolingThresholdTemperature', Characteristic.CoolingThresholdTemperature,\n                    config.topics.setCoolingThresholdTemperature, config.topics.getCoolingThresholdTemperature, 25 );\n\n                tempRange( service, Characteristic.CoolingThresholdTemperature );\n            }\n\n            // Characteristic.RelativeHumidityDehumidifierThreshold\n            function characteristic_RelativeHumidityDehumidifierThreshold( service ) {\n                floatCharacteristic( service, 'relativeHumidityDehumidifierThreshold', Characteristic.RelativeHumidityDehumidifierThreshold, config.topics.setRelativeHumidityDehumidifierThreshold, config.topics.getRelativeHumidityDehumidifierThreshold, 0 );\n            }\n\n            // Characteristic.HeatingThresholdTemperature\n            function characteristic_HeatingThresholdTemperature( service ) {\n                floatCharacteristic( service, 'heatingThresholdTemperature', Characteristic.HeatingThresholdTemperature,\n                    config.topics.setHeatingThresholdTemperature, config.topics.getHeatingThresholdTemperature, 20 );\n\n                tempRange( service, Characteristic.HeatingThresholdTemperature );\n            }\n\n            // Characteristic.CurrentRelativeHumidity\n            function characteristic_CurrentRelativeHumidity( service ) {\n                floatCharacteristic( service, 'currentRelativeHumidity', Characteristic.CurrentRelativeHumidity,\n                    null, config.topics.getCurrentRelativeHumidity, 0 );\n            }\n\n            // Characteristic.TargetRelativeHumidity\n            function characteristic_TargetRelativeHumidity( service ) {\n                floatCharacteristic( service, 'targetRelativeHumidity', Characteristic.TargetRelativeHumidity,\n                    config.topics.setTargetRelativeHumidity, config.topics.getTargetRelativeHumidity, 0 );\n            }\n\n            // History for CurrentRelativeHumidity (Eve-only)\n            function history_CurrentRelativeHumidity( historySvc ) {\n                if( config.topics.getCurrentRelativeHumidity ) {\n                    // additional MQTT subscription instead of set-callback due to correct averaging:\n                    mqttSubscribe( config.topics.getCurrentRelativeHumidity, 'currentRelativeHumidity', function( topic, message ) {\n                        var logEntry = {\n                            time: Math.floor( Date.now() / 1000 ),  // seconds (UTC)\n                            humidity: parseFloat( message )  // fakegato-history logProperty 'humidity' for humidity sensor\n                        };\n                        historySvc.addEntry( logEntry );\n                    } );\n                }\n            }\n\n            // Characteristic.characteristic_AirPressure (Eve-only)\n            function characteristic_AirPressure( service ) {\n                floatCharacteristic( service, 'airPressure', Eve.Characteristics.AirPressure, null, config.topics.getAirPressure, 700 );\n                // set characteristic Elevation for air pressure calibration (not used yet with MQTT)\n                service.updateCharacteristic( Eve.Characteristics.Elevation, 100 );\n            }\n\n            // History for AirPressure (Eve-only)\n            function history_AirPressure( historySvc ) {\n                if( config.topics.getAirPressure ) {\n                    // additional MQTT subscription instead of set-callback due to correct averaging:\n                    mqttSubscribe( config.topics.getAirPressure, 'airPressure', function( topic, message ) {\n                        var logEntry = {\n                            time: Math.floor( Date.now() / 1000 ),  // seconds (UTC)\n                            pressure: parseFloat( message )  // fakegato-history logProperty 'pressure' for air pressure sensor\n                        };\n                        historySvc.addEntry( logEntry );\n                    } );\n                }\n            }\n\n            // Characteristic.WeatherCondition (Eve-only)\n            function characteristic_WeatherCondition( service ) {\n                service.addOptionalCharacteristic( Eve.Characteristics.Condition ); // to avoid warnings\n                stringCharacteristic( service, 'weatherCondition', Eve.Characteristics.Condition, null, config.topics.getWeatherCondition, '-' );\n            }\n\n            // Characteristic.Rain1h (Eve-only)\n            function characteristic_Rain1h( service ) {\n                service.addOptionalCharacteristic( Eve.Characteristics.Rain1h ); // to avoid warnings\n                integerCharacteristic( service, 'rain1h', Eve.Characteristics.Rain1h, null, config.topics.getRain1h );\n            }\n\n            // Characteristic.Rain24h (Eve-only)\n            function characteristic_Rain24h( service ) {\n                service.addOptionalCharacteristic( Eve.Characteristics.Rain24h ); // to avoid warnings\n                integerCharacteristic( service, 'rain24h', Eve.Characteristics.Rain24h, null, config.topics.getRain24h );\n            }\n\n            // Characteristic.UVIndex (Eve-only)\n            function characteristic_UVIndex( service ) {\n                service.addOptionalCharacteristic( Eve.Characteristics.UvIndex ); // to avoid warnings\n                integerCharacteristic( service, 'uvIndex', Eve.Characteristics.UvIndex, null, config.topics.getUVIndex );\n            }\n\n            // Characteristic.Visibility (Eve-only)\n            function characteristic_Visibility( service ) {\n                service.addOptionalCharacteristic( Eve.Characteristics.Visibility ); // to avoid warnings\n                integerCharacteristic( service, 'visibility', Eve.Characteristics.Visibility, null, config.topics.getVisibility );\n            }\n\n            // Characteristic.WindDirection (Eve-only)\n            function characteristic_WindDirection( service ) {\n                service.addOptionalCharacteristic( Eve.Characteristics.WindDirection ); // to avoid warnings\n                stringCharacteristic( service, 'windDirection', Eve.Characteristics.WindDirection, null, config.topics.getWindDirection, '-' );\n            }\n\n            // Characteristic.WindSpeed (Eve-only)\n            function characteristic_WindSpeed( service ) {\n                service.addOptionalCharacteristic( Eve.Characteristics.WindSpeed ); // to avoid warnings\n                floatCharacteristic( service, 'windSpeed', Eve.Characteristics.WindSpeed, null, config.topics.getWindSpeed, 0 );\n            }\n\n            // Characteristic.maxWind (Eve-only)\n            function characteristic_MaximumWindSpeed( service ) {\n                service.addOptionalCharacteristic( Eve.Characteristics.MaximumWindSpeed ); // to avoid warnings\n                floatCharacteristic( service, 'maxWind', Eve.Characteristics.MaximumWindSpeed, null, config.topics.getmaxWind, 0 );\n            }\n\n            // Characteristic.Dewpoint(Eve-only)\n            function characteristic_DewPoint( service ) {\n                service.addOptionalCharacteristic( Eve.Characteristics.DewPoint ); // to avoid warnings\n                floatCharacteristic( service, 'DewPoint', Eve.Characteristics.DewPoint, null, config.topics.getDewPoint, 0 );\n            }\n\n            // Characteristic.ContactSensorState\n            function characteristic_ContactSensorState( service ) {\n                booleanCharacteristic( service, 'contactSensorState', Characteristic.ContactSensorState,\n                    null, config.topics.getContactSensorState, false, function( val ) {\n                        return val ? Characteristic.ContactSensorState.CONTACT_NOT_DETECTED : Characteristic.ContactSensorState.CONTACT_DETECTED;\n                    }, undefined, config.resetStateAfterms );\n            }\n\n            // History for ContactSensorState (Eve-only)\n            function history_ContactSensorState( historySvc, service ) {\n                characteristic_LastActivation( historySvc, service );\n\n                // get characteristic to be logged\n                var charac = service.getCharacteristic( Characteristic.ContactSensorState );\n\n                // counterFile for saving 'timesOpened' and 'resetTotal'\n                const counterFile = historyCounterFile();\n\n                function writeCounterFile() {\n                    let saveObj = { timesOpened: state.timesOpened, resetTotal: state.resetTotal };\n                    fs.writeFile( counterFile, JSON.stringify( saveObj ), 'utf8', function( err ) {\n                        if( err ) {\n                            log( 'Error: cannot write file to save timesOpened' );\n                        }\n                    } );\n                }\n\n                // load TimesOpened counter from counterFile\n                fs.readFile( counterFile, 'utf8', function( err, data ) {\n                    let cnt = 0;\n                    let res = Math.floor( Date.now() / 1000 ) - 978307200;  // seconds since 01.01.2001\n                    if( err ) {\n                        log.debug( 'No data loaded for TimesOpened' );\n                    } else {\n                        cnt = JSON.parse( data ).timesOpened;\n                        res = JSON.parse( data ).resetTotal;\n                    }\n                    service.addOptionalCharacteristic( Eve.Characteristics.TimesOpened ); // to avoid warnings\n                    addCharacteristic( service, 'timesOpened', Eve.Characteristics.TimesOpened, cnt );\n                    historySvc.addOptionalCharacteristic( Eve.Characteristics.ResetTotal ); // to avoid warnings\n                    addCharacteristic( historySvc, 'resetTotal', Eve.Characteristics.ResetTotal, res, function() {\n                        state.timesOpened = 0; // reset counter\n                        service.updateCharacteristic( Eve.Characteristics.TimesOpened, 0 );\n                        writeCounterFile();\n                        log( \"Reset TimesOpened to 0\" );\n                    } );\n\n                    // these ones are necessary to display history for contact sensors\n                    service.addOptionalCharacteristic( Eve.Characteristics.OpenDuration ); // to avoid warnings\n                    addCharacteristic( service, 'openDuration', Eve.Characteristics.OpenDuration, 0 );\n                    service.addOptionalCharacteristic( Eve.Characteristics.ClosedDuration ); // to avoid warnings\n                    addCharacteristic( service, 'closedDuration', Eve.Characteristics.ClosedDuration, 0 );\n\n                    // attach change callback for this characteristic\n                    charac.on( 'change', function( obj ) {\n                        var logEntry = {\n                            time: Math.floor( Date.now() / 1000 ),  // seconds (UTC)\n                            status: obj.newValue  // fakegato-history logProperty 'status' for contact sensor\n                        };\n                        // update Eve's Characteristic.LastActivation\n                        state.lastActivation = logEntry.time - historySvc.getInitialTime();\n                        service.updateCharacteristic( Eve.Characteristics.LastActivation, state.lastActivation );\n                        if( logEntry.status ) {\n                            // update Eve's Characteristic.TimesOpened\n                            state.timesOpened++;\n                            service.updateCharacteristic( Eve.Characteristics.TimesOpened, state.timesOpened );\n                            writeCounterFile();\n                        }\n                        historySvc.addEntry( logEntry );\n                    } );\n                } );\n            }\n\n            // Characteristic.ProgrammableSwitchEvent\n            function characteristic_ProgrammableSwitchEvent( service, property, getTopic, switchValues, restrictSwitchValues ) {\n                let values = switchValues;\n                if( !values ) {\n                    values = [ '1', '2', 'L' ]; // 1 means SINGLE_PRESS, 2 means DOUBLE_PRESS, L means LONG_PRESS\n                }\n                multiCharacteristic( service, property, Characteristic.ProgrammableSwitchEvent, null, getTopic, values, null, true );\n                if( restrictSwitchValues ) {\n                    let characteristic = service.getCharacteristic( Characteristic.ProgrammableSwitchEvent );\n                    characteristic.props.validValues = restrictSwitchValues;\n                }\n            }\n\n            // Characteristic.Volume\n            function characteristic_Volume( service ) {\n                floatCharacteristic( service, 'volume', Characteristic.Volume, config.topics.setVolume, config.topics.getVolume, 0 );\n            }\n\n            // Characteristic.Mute\n            function characteristic_Mute( service ) {\n                booleanCharacteristic( service, 'mute', Characteristic.Mute, config.topics.setMute, config.topics.getMute, false );\n            }\n\n            // Characteristic.SecuritySystemCurrentState\n            function characteristic_SecuritySystemCurrentState( service ) {\n                let values = config.currentStateValues;\n                if( !values ) {\n                    values = [ 'SA', 'AA', 'NA', 'D', 'T' ];\n                }\n                multiCharacteristic( service, 'currentState', Characteristic.SecuritySystemCurrentState, null, config.topics.getCurrentState, values, Characteristic.SecuritySystemCurrentState.DISARMED );\n            }\n\n            // Characteristic.SecuritySystemTargetState\n            function characteristic_SecuritySystemTargetState( service ) {\n                let values = config.targetStateValues;\n                if( !values ) {\n                    values = [ 'SA', 'AA', 'NA', 'D' ];\n                }\n                multiCharacteristic( service, 'targetState', Characteristic.SecuritySystemTargetState, config.topics.setTargetState, config.topics.getTargetState, values, Characteristic.SecuritySystemTargetState.DISARM );\n                if( config.restrictTargetState ) {\n                    let characteristic = service.getCharacteristic( Characteristic.SecuritySystemTargetState );\n                    characteristic.props.validValues = config.restrictTargetState;\n                }\n            }\n\n            // Characteristic.SmokeDetected\n            function characteristic_SmokeDetected( service ) {\n                booleanCharacteristic( service, 'smokeDetected', Characteristic.SmokeDetected,\n                    null, config.topics.getSmokeDetected, false, function( val ) {\n                        return val ? Characteristic.SmokeDetected.SMOKE_DETECTED : Characteristic.SmokeDetected.SMOKE_NOT_DETECTED;\n                    }, undefined, config.resetStateAfterms );\n            }\n\n            // Characteristic.CurrentDoorState\n            function characteristic_CurrentDoorState( service ) {\n                let values = config.doorCurrentValues || config.doorValues;\n                if( !values ) {\n                    values = [ 'O', 'C', 'o', 'c', 'S' ];\n                }\n                multiCharacteristic( service, 'currentDoorState', Characteristic.CurrentDoorState, null, config.topics.getCurrentDoorState, values, Characteristic.CurrentDoorState.CLOSED );\n            }\n\n            function characteristic_SimpleCurrentDoorState( service, property, getTopic, initialValue, mapValueFunc ) {\n                // set up characteristic\n                var charac = service.getCharacteristic( Characteristic.CurrentDoorState );\n                charac.on( 'get', function( callback ) {\n                    handleGetStateCallback( callback, mapValueFunc( state[ property ] ) );\n                } );\n\n                // property-changed handler\n                let propChangedHandler = events.targetDoorState = function() {\n                    setTimeout( () => {\n                        setCharacteristic( charac, mapValueFunc( state[ property ] ) );\n                    }, 1000 );\n                };\n\n                // set initial value\n                state[ property ] = initialValue;\n                propChangedHandler();\n\n                // subscribe to get topic\n                if( getTopic ) {\n                    mqttSubscribe( getTopic, property, function( topic, message ) {\n                        // determine whether this is an on or off value\n                        let newState = false; // assume off\n                        if( isRecvValueOn( message ) ) {\n                            newState = true; // received on value so on\n                        } else if( !isRecvValueOff( message ) ) {\n                            // received value NOT acceptable as 'off' so ignore message\n                            return;\n                        }\n\n                        // if changed, set\n                        if( state[ property ] != newState ) {\n                            state[ property ] = newState;\n                            propChangedHandler();\n                        }\n                    } );\n                }\n            }\n\n            // Characteristic.DoorMoving (mqttthing simplified state)\n            function characteristic_DoorMoving( service ) {\n                characteristic_SimpleCurrentDoorState( service, 'doorMoving', config.topics.getDoorMoving, false, ( isMoving ) => {\n                    if( isMoving ) {\n                        if( state.targetDoorState == Characteristic.TargetDoorState.OPEN ) {\n                            return Characteristic.CurrentDoorState.OPENING;\n                        } else {\n                            return Characteristic.CurrentDoorState.CLOSING;\n                        }\n                    } else {\n                        if( state.targetDoorState == Characteristic.TargetDoorState.OPEN ) {\n                            return Characteristic.CurrentDoorState.OPEN;\n                        } else {\n                            return Characteristic.CurrentDoorState.CLOSED;\n                        }\n                    }\n                } );\n            }\n\n            // Characteristic.TargetDoorState\n            function characteristic_TargetDoorState( service ) {\n                let values = config.doorTargetValues || config.doorValues;\n                if( !values ) {\n                    values = [ 'O', 'C' ];\n                }\n                multiCharacteristic( service, 'targetDoorState', Characteristic.TargetDoorState, config.topics.setTargetDoorState, config.topics.getTargetDoorState, values, Characteristic.TargetDoorState.OPEN );\n            }\n\n            // Characteristic.ObstructionDetected\n            function characteristic_ObstructionDetected( service ) {\n                booleanCharacteristic( service, 'obstructionDetected', Characteristic.ObstructionDetected, null, config.topics.getObstructionDetected, false );\n            }\n\n            // Characteristic.LockCurrentState\n            function characteristic_LockCurrentState( service ) {\n                let values = config.lockValues;\n                if( !values ) {\n                    values = [ 'U', 'S', 'J', '?' ];\n                }\n                multiCharacteristic( service, 'lockCurrentState', Characteristic.LockCurrentState, null, config.topics.getLockCurrentState, values, Characteristic.LockCurrentState.UNSECURED );\n            }\n\n            // Characteristic.LockTargetState\n            function characteristic_LockTargetState( service ) {\n                let values = config.lockValues;\n                if( !values ) {\n                    values = [ 'U', 'S' ];\n                }\n                multiCharacteristic( service, 'lockTargetState', Characteristic.LockTargetState, config.topics.setLockTargetState, config.topics.getLockTargetState, values, Characteristic.LockTargetState.UNSECURED );\n            }\n\n            // Characteristic.RotationDirection\n            function characteristic_RotationDirection( service ) {\n                integerCharacteristic( service, 'rotationDirection', Characteristic.RotationDirection, config.topics.setRotationDirection, config.topics.getRotationDirection );\n            }\n\n            // Characteristic.RotationSpeed\n            function characteristic_RotationSpeed( service, handleOn ) {\n\n                if( config.topics.setOn || !handleOn ) {\n                    // separate On topic, or we're not handling 'On', so implement standard rotationSpeed characteristic\n                    integerCharacteristic( service, 'rotationSpeed', Characteristic.RotationSpeed, config.topics.setRotationSpeed, config.topics.getRotationSpeed,\n                        { minValue: config.minRotationSpeed, maxValue: config.maxRotationSpeed } );\n                } else {\n                    // no separate On topic, so use RotationSpeed 0 to indicate Off state...\n\n                    // subscription\n                    if( config.topics.getRotationSpeed ) {\n                        mqttSubscribe( config.topics.getRotationSpeed, 'rotationSpeed', ( topic, message ) => {\n                            let newState = parseInt( message );\n                            let newOn = ( newState != 0 );\n                            if( state.rotationSpeed != newState || state.on != newOn ) {\n                                if( newOn ) {\n                                    state.rotationSpeed = newState;\n                                    setCharacteristic( service.getCharacteristic( Characteristic.RotationSpeed ), newState );\n                                }\n                                state.on = newOn;\n                                setCharacteristic( service.getCharacteristic( Characteristic.On ), newState != 0 );\n                            }\n                        } );\n                    }\n\n                    // publishing (throttled)\n                    let publishNow = function() {\n                        let rot = state.rotationSpeed;\n                        if( !config.topics.setOn && !state.on ) {\n                            rot = 0;\n                        }\n                        mqttPublish( config.topics.setRotationSpeed, 'rotationSpeed', rot );\n                    };\n\n                    let publish = () => throttledCall( publishNow, 'rotationSpeed_pub', 20 );\n\n                    // RotationSpeed characteristic\n                    addCharacteristic( service, 'rotationSpeed', Characteristic.RotationSpeed, 0, () => {\n                        if( state.rotationSpeed > 0 && !state.on ) {\n                            state.on = true;\n                        }\n                        publish();\n                    } );\n\n                    // On Characteristic\n                    addCharacteristic( service, 'on', Characteristic.On, false, function() {\n                        if( state.on && state.rotationSpeed == 0 ) {\n                            state.rotationSpeed = 100;\n                        }\n                        publish();\n                    } );\n                }\n            }\n\n            // Characteristic.BatteryLevel\n            function characteristic_BatteryLevel( service ) {\n                integerCharacteristic( service, 'batteryLevel', Characteristic.BatteryLevel, null, config.topics.getBatteryLevel );\n            }\n\n            // Characteristic.ChargingState\n            function characteristic_ChargingState( service ) {\n                let values = config.chargingStateValues;\n                if( !values ) {\n                    values = [ 'NOT_CHARGING', 'CHARGING', 'NOT_CHARGEABLE' ];\n                }\n                multiCharacteristic( service, 'chargingState', Characteristic.ChargingState, null, config.topics.getChargingState, values, Characteristic.ChargingState.NOT_CHARGING );\n            }\n\n            // Characteristic.LeakDetected\n            function characteristic_LeakDetected( service ) {\n                booleanCharacteristic( service, 'leakDetected', Characteristic.LeakDetected, null, config.topics.getLeakDetected, false, function( val ) {\n                    return val ? Characteristic.LeakDetected.LEAK_DETECTED : Characteristic.LeakDetected.LEAK_NOT_DETECTED;\n                }, undefined, config.resetStateAfterms );\n            }\n\n            // Characteristic.WaterLevel\n            function characteristic_WaterLevel( service ) {\n                let options = { minValue: 0, maxValue: 100 };\n                integerCharacteristic( service, 'waterLevel', Characteristic.WaterLevel, config.topics.setWaterLevel, config.topics.getWaterLevel, options );\n            }\n\n            // Characteristic.TargetPosition\n            function characteristic_TargetPosition( service ) {\n                integerCharacteristic( service, 'targetPosition', Characteristic.TargetPosition, config.topics.setTargetPosition, config.topics.getTargetPosition, {\n                    initialValue: config.minPosition || 0,\n                    minValue: config.minPosition,\n                    maxValue: config.maxPosition\n                } );\n            }\n\n            // Characteristic.CurrentPosition\n            function characteristic_CurrentPosition( service ) {\n                integerCharacteristic( service, 'currentPosition', Characteristic.CurrentPosition, null, config.topics.getCurrentPosition, {\n                    initialValue: config.minPosition || 0,\n                    minValue: config.minPosition,\n                    maxValue: config.maxPosition\n                } );\n            }\n\n            // Characteristic.PositionState\n            function characteristic_PositionState( service ) {\n                let values = config.positionStateValues;\n                if( !values ) {\n                    values = [ 'DECREASING', 'INCREASING', 'STOPPED' ];\n                }\n                multiCharacteristic( service, 'positionState', Characteristic.PositionState, null, config.topics.getPositionState, values, Characteristic.PositionState.STOPPED );\n            }\n\n            // Characteristic.HoldPosition\n            function characteristic_HoldPosition( service ) {\n                booleanCharacteristic( service, 'holdPosition', Characteristic.HoldPosition, config.topics.setHoldPosition, null, false );\n            }\n\n            // Characteristic.TargetHorizontalTiltAngle\n            function Characteristic_TargetHorizontalTiltAngle( service ) {\n                integerCharacteristic( service, 'targetHorizontalTiltAngle', Characteristic.TargetHorizontalTiltAngle, config.topics.setTargetHorizontalTiltAngle, config.topics.getTargetHorizontalTiltAngle );\n            }\n\n            // Characteristic.CurrentHorizontalTiltAngle\n            function Characteristic_CurrentHorizontalTiltAngle( service ) {\n                integerCharacteristic( service, 'currentHorizontalTiltAngle', Characteristic.CurrentHorizontalTiltAngle, null, config.topics.getCurrentHorizontalTiltAngle );\n            }\n\n            // Characteristic.TargetVerticalTiltAngle\n            function Characteristic_TargetVerticalTiltAngle( service ) {\n                integerCharacteristic( service, 'targetVerticalTiltAngle', Characteristic.TargetVerticalTiltAngle, config.topics.setTargetVerticalTiltAngle, config.topics.getTargetVerticalTiltAngle );\n            }\n\n            // Characteristic.CurrentVerticalTiltAngle\n            function Characteristic_CurrentVerticalTiltAngle( service ) {\n                integerCharacteristic( service, 'currentVerticalTiltAngle', Characteristic.CurrentVerticalTiltAngle, null, config.topics.getCurrentVerticalTiltAngle );\n            }\n\n            // Characteristic.AirQuality\n            function characteristic_AirQuality( service ) {\n                let values = config.airQualityValues;\n                if( !values ) {\n                    values = [ 'UNKNOWN', 'EXCELLENT', 'GOOD', 'FAIR', 'INFERIOR', 'POOR' ];\n                }\n                multiCharacteristic( service, 'airQuality', Characteristic.AirQuality, null, config.topics.getAirQuality, values, Characteristic.AirQuality.UNKNOWN );\n            }\n\n            // Characteristic.PM10Density\n            function characteristic_PM10Density( service ) {\n                floatCharacteristic( service, 'pm10density', Characteristic.PM10Density, null, config.topics.getPM10Density, Characteristic.PM10Density.UNKNOWN );\n            }\n\n            // Characteristic.PM2_5Density\n            function characteristic_PM2_5Density( service ) {\n                floatCharacteristic( service, 'pm2_5density', Characteristic.PM2_5Density, null, config.topics.getPM2_5Density, Characteristic.PM2_5Density.UNKNOWN );\n            }\n\n            // Characteristic.OzoneDensity\n            function characteristic_OzoneDensity( service ) {\n                floatCharacteristic( service, 'ozoneDensity', Characteristic.OzoneDensity, null, config.topics.getOzoneDensity );\n            }\n\n            // Characteristic.NitrogenDioxideDensity\n            function characteristic_NitrogenDioxideDensity( service ) {\n                floatCharacteristic( service, 'nitrogenDioxideDensity', Characteristic.NitrogenDioxideDensity, null, config.topics.getNitrogenDioxideDensity );\n            }\n\n            // Characteristic.SulphurDioxideDensity\n            function characteristic_SulphurDioxideDensity( service ) {\n                floatCharacteristic( service, 'sulphurDioxideDensity', Characteristic.SulphurDioxideDensity, null, config.topics.getSulphurDioxideDensity );\n            }\n\n            // Characteristic.VOCDensity\n            function characteristic_VOCDensity( service ) {\n                floatCharacteristic( service, 'VOCDensity', Characteristic.VOCDensity, null, config.topics.getVOCDensity );\n            }\n\n            // Characteristic.CarbonMonoxideLevel\n            function characteristic_CarbonMonoxideLevel(service) {\n                floatCharacteristic(service, 'carbonMonoxideLevel', Characteristic.CarbonMonoxideLevel, null, config.topics.getCarbonMonoxideLevel);\n            }\n            // Characteristic.CarbonMonoxideDetected\n            function characteristic_CarbonMonoxideDetected(service) {\n                    let values = config.carbonMonoxideDetectedValues;\n                    if (!values) {\n                            values =['NORMAL', 'ABNORMAL'];\n                        }\n                    multiCharacteristic(service, 'carbonMonoxideDetected', Characteristic.CarbonMonoxideDetected, null, config.topics.getCarbonMonoxideDetected, values, Characteristic.CarbonMonoxideDetected.CO_LEVELS_NORMAL);\n                }\n\n            // Characteristic.CarbonMonoxidePeakLevel\n            function characteristic_CarbonMonoxidePeakLevel(service) {\n                    floatCharacteristic(service, 'carbonMonoxidePeakLevel', Characteristic.CarbonMonoxidePeakLevel, null, config.topics.getCarbonMonoxidePeakLevel, 0);\n                }\n\n            // Eve.Characteristics.AirParticulateDensity (Eve-only)\n            function characteristic_AirQualityPPM( service ) {\n                service.addOptionalCharacteristic( Eve.Characteristics.AirParticulateDensity ); // to avoid warnings\n                floatCharacteristic( service, 'airQualityPPM', Eve.Characteristics.AirParticulateDensity, null, config.topics.getAirQualityPPM );\n            }\n\n            // Eve.Characteristics.EveTemperatureDisplayUnits (Eve Room 2 only)\n            // Defined with airQualitySensor support for room2 by D4rk (used if config.history && config.room2) but gives warning so removing for now and calling original characteristic_TemperatureDisplayUnits instead...\n            // > This plugin generated a warning from the characteristic 'Temperature Display Units': characteristic value expected valid finite number and received \"NaN\" (number). See https://git.io/JtMGR for more info.\n            // TODO: confirm with D4rk\n            /*\n            function characteristic_EveTemperatureDisplayUnits( service ) {\n                stringCharacteristic( service, 'eveTemperatureDisplayUnits', Characteristic.TemperatureDisplayUnits, null, null, 'Celsius' )\n            }\n            */\n\n            // History for Air Quality (Eve-only)\n            function history_AirQualityPPM( historySvc ) {\n                if( config.topics.getAirQualityPPM ) {\n                    // additional MQTT subscription instead of set-callback due to correct averaging:\n                    mqttSubscribe( config.topics.getAirQualityPPM, 'airQualityPPM', function( topic, message ) {\n                        var logEntry = {\n                            time: Math.floor( Date.now() / 1000 ),  // seconds (UTC)\n                            ppm: parseFloat( message )  // fakegato-history logProperty 'ppm' for air quality sensor\n                        };\n                        historySvc.addEntry( logEntry );\n                    } );\n                }\n            }\n\n            // History for Air Quality (Eve Room 2 only)\n            function history_VOCDensity( historySvc ) {\n                if( config.topics.getVOCDensity ) {\n                    // additional MQTT subscription instead of set-callback due to correct averaging:\n                    mqttSubscribe( config.topics.getVOCDensity, 'VOCDensity', function( topic, message ) {\n                        var logEntry = {\n                            time: Math.floor( Date.now() / 1000 ),  // seconds (UTC)\n                            voc: parseFloat( message )  // fakegato-history logProperty 'voc' for air quality sensor\n                        };\n                        historySvc.addEntry( logEntry );\n                    } );\n                }\n            }\n\n            // Characteristic.CarbonDioxideDetected\n            function characteristic_CarbonDioxideDetected( service ) {\n                let values = config.carbonDioxideDetectedValues;\n                if( !values ) {\n                    values = [ 'NORMAL', 'ABNORMAL' ];\n                }\n                multiCharacteristic( service, 'carbonDioxideDetected', Characteristic.CarbonDioxideDetected, null, config.topics.getCarbonDioxideDetected, values, Characteristic.CarbonDioxideDetected.CO2_LEVELS_NORMAL );\n            }\n\n            // Characteristic.CarbonDioxideLevel\n            function characteristic_CarbonDioxideLevel( service ) {\n                floatCharacteristic( service, 'carbonDioxideLevel', Characteristic.CarbonDioxideLevel, null, config.topics.getCarbonDioxideLevel, 0 );\n            }\n\n            // Characteristic.CarbonDioxideLevel\n            function characteristic_CarbonDioxidePeakLevel( service ) {\n                floatCharacteristic( service, 'carbonDioxidePeakLevel', Characteristic.CarbonDioxidePeakLevel, null, config.topics.getCarbonDioxidePeakLevel, 0 );\n            }\n\n            // Characteristic.CurrentHeatingCoolingState\n            function characteristic_CurrentHeatingCoolingState( service ) {\n                let values = config.heatingCoolingStateValues;\n                if( !values ) {\n                    values = [ 'OFF', 'HEAT', 'COOL' ];\n                }\n                multiCharacteristic( service, 'currentHeatingCoolingState', Characteristic.CurrentHeatingCoolingState, null, config.topics.getCurrentHeatingCoolingState, values, Characteristic.CurrentHeatingCoolingState.OFF );\n            }\n\n            // Characteristic.TargetHeatingCoolingState\n            function characteristic_TargetHeatingCoolingState( service ) {\n                let values = config.heatingCoolingStateValues;\n                if( !values ) {\n                    values = [ 'OFF', 'HEAT', 'COOL', 'AUTO' ];\n                }\n                multiCharacteristic( service, 'targetHeatingCoolingState', Characteristic.TargetHeatingCoolingState, config.topics.setTargetHeatingCoolingState, config.topics.getTargetHeatingCoolingState, values, Characteristic.TargetHeatingCoolingState.OFF );\n                if( config.restrictHeatingCoolingState ) {\n                    let characteristic = service.getCharacteristic( Characteristic.TargetHeatingCoolingState );\n                    characteristic.props.validValues = config.restrictHeatingCoolingState;\n                }\n            }\n\n            // Characteristic.CurrentHeaterCoolerState\n            function characteristic_CurrentHeaterCoolerState( service ) {\n                let values = config.currentHeaterCoolerValues;\n                if( !values ) {\n                    values = [ 'INACTIVE', 'IDLE', 'HEATING', 'COOLING' ];\n                }\n                multiCharacteristic( service, 'currentHeaterCoolerState', Characteristic.CurrentHeaterCoolerState, null, config.topics.getCurrentHeaterCoolerState, values, Characteristic.CurrentHeaterCoolerState.INACTIVE );\n            }\n\n            // Characteristic.TargetHeaterCoolerState\n            function characteristic_TargetHeaterCoolerState( service ) {\n                let values = config.targetHeaterCoolerValues;\n                if( !values ) {\n                    values = [ 'AUTO', 'HEAT', 'COOL' ];\n                }\n                multiCharacteristic( service, 'targetHeaterCoolerState', Characteristic.TargetHeaterCoolerState, config.topics.setTargetHeaterCoolerState, config.topics.getTargetHeaterCoolerState, values, Characteristic.TargetHeaterCoolerState.AUTO );\n                if( config.restrictHeaterCoolerState ) {\n                    let characteristic = service.getCharacteristic( Characteristic.TargetHeaterCoolerState );\n                    characteristic.props.validValues = config.restrictHeaterCoolerState;\n                }\n            }\n\n            // Characteristic.TargetHumidifierDehumidifierState\n            function characteristic_TargetHumidifierDehumidifierState( service ) {\n                let values = config.targetHumidifierDehumidifierState;\n                if( !values ) {\n                    values = [ 'HUMIDIFIER_OR_DEHUMIDIFIER', 'HUMIDIFIER', 'DEHUMIDIFIER' ];\n                }\n                multiCharacteristic( service, 'targetHumidifierDehumidifierState', Characteristic.TargetHumidifierDehumidifierState, config.topics.setTargetHumidifierDehumidifierState, config.topics.getTargetHumidifierDehumidifierState, values, Characteristic.TargetHumidifierDehumidifierState.DEHUMIDIFIER );\n                if( config.restrictDehumidifierState ) {\n                    let characteristic = service.getCharacteristic( Characteristic.TargetDehumidifierState );\n                    characteristic.props.validValues = config.restrictDehumidifierState;\n                }\n            }\n\n            // Characteristic.CurrentHumidifierDehumidifierState\n            function characteristic_CurrentHumidifierDehumidifierState( service ) {\n                let values = config.currentHumidifierDehumidifierState;\n                if( !values ) {\n                    values = [ 'INACTIVE', 'IDLE', 'HUMIDIFYING', 'DEHUMIDIFYING' ];\n                }\n                multiCharacteristic( service, 'currentHumidifierDehumidifierState', Characteristic.CurrentHumidifierDehumidifierState, null, config.topics.getCurrentHumidifierDehumidifierState, values, Characteristic.CurrentHumidifierDehumidifierState.INACTIVE );\n            }\n\n            // Characteristic.CurrentAirPurifierState\n            function characteristic_CurrentAirPurifierState( service ) {\n                let values = config.currentAirPurifierStateValues;\n                if( !values ) {\n                    values = [ 'INACTIVE', 'IDLE', 'PURIFYING' ];\n                }\n                multiCharacteristic( service, 'currentAirPurifierState', Characteristic.CurrentAirPurifierState, null, config.topics.getCurrentAirPurifierState, values, Characteristic.CurrentAirPurifierState.INACTIVE );\n            }\n\n            // Characteristic.TargetAirPurifierState\n            function characteristic_TargetAirPurifierState( service ) {\n                let values = config.targetAirPurifierStateValues;\n                if( !values ) {\n                    values = [ 'MANUAL', 'AUTO' ];\n                }\n                multiCharacteristic( service, 'targetAirPurifierState', Characteristic.TargetAirPurifierState, config.topics.setTargetAirPurifierState, config.topics.getTargetAirPurifierState, values, Characteristic.TargetAirPurifierState.AUTO );\n            }\n            // Characteristic.CurrentFanState\n            function characteristic_CurrentFanState(service) {\n                let values = config.currentFanValues;\n                if (!values) {\n                    values = ['INACTIVE', 'IDLE', 'BLOWING_AIR'];\n                }\n                multiCharacteristic(service, 'currentFanState', Characteristic.CurrentFanState, null, config.topics.getCurrentFanState, values, Characteristic.CurrentFanState.INACTIVE);\n            }\n\n            // Characteristic.TargetFanState\n            function characteristic_TargetFanState(service) {\n                let values = config.targetFanStateValues;\n                if (!values) {\n                    values = ['MANUAL', 'AUTO'];\n                }\n                multiCharacteristic(service, 'targetFanState', Characteristic.TargetFanState, config.topics.setTargetFanState, config.topics.getTargetFanState, values, Characteristic.TargetFanState.AUTO);\n            }\n\n            // Characteristic.LockPhysicalControls\n            function characteristic_LockPhysicalControls( service ) {\n                let values = config.lockPhysicalControlsValues;\n                if( !values ) {\n                    values = [ 'DISABLED', 'ENABLED' ];\n                }\n                multiCharacteristic( service, 'lockPhysicalControls', Characteristic.LockPhysicalControls, config.topics.setLockPhysicalControls, config.topics.getLockPhysicalControls, values, Characteristic.LockPhysicalControls.CONTROL_LOCK_DISABLED );\n            }\n\n            // Characteristic.SwingMode\n            function characteristic_SwingMode( service ) {\n                let values = config.swingModeValues;\n                if( !values ) {\n                    values = [ 'DISABLED', 'ENABLED' ];\n                }\n                multiCharacteristic( service, 'swingMode', Characteristic.SwingMode, config.topics.setSwingMode, config.topics.getSwingMode, values, Characteristic.SwingMode.SWING_DISABLED );\n            }\n\n            // Characteristic.TemperatureDisplayUnits\n            function characteristic_TemperatureDisplayUnits( service ) {\n                let values = config.temperatureDisplayUnitsValues;\n                if( !values ) {\n                    values = [ 'CELSIUS', 'FAHRENHEIT' ];\n                }\n                multiCharacteristic( service, 'temperatureDisplayUnits', Characteristic.TemperatureDisplayUnits,\n                    config.topics.setTemperatureDisplayUnits, config.topics.getTemperatureDisplayUnits, values,\n                    Characteristic.TemperatureDisplayUnits.CELSIUS );\n            }\n\n            // Characteristic.ServiceLabelIndex\n            function characteristic_ServiceLabelIndex( service, index ) {\n                service.setCharacteristic( Characteristic.ServiceLabelIndex, index );\n            }\n\n            // Characteristic.ServiceLabelNamespace\n            function characteristic_ServiceLabelNamespace( service ) {\n                if( config.labelType === 'dots' ) {\n                    service.setCharacteristic( Characteristic.ServiceLabelNamespace, Characteristic.ServiceLabelNamespace.DOTS );\n                } else if( config.labelType === 'numerals' ) {\n                    service.setCharacteristic( Characteristic.ServiceLabelNamespace, Characteristic.ServiceLabelNamespace.ARABIC_NUMERALS );\n                } else {\n                    service.setCharacteristic( Characteristic.ServiceLabelNamespace, Characteristic.ServiceLabelNamespace.DOTS );\n                }\n            }\n\n            // Eve.Characteristics.CurrentConsumption [Watts] (Eve-only)\n            function characteristic_CurrentConsumption( service ) {\n                service.addOptionalCharacteristic( Eve.Characteristics.CurrentConsumption ); // to avoid warnings\n                floatCharacteristic( service, 'currentConsumption', Eve.Characteristics.CurrentConsumption, null, config.topics.getWatts, 0 );\n            }\n\n            // Eve.Characteristics.Voltage [Volts] (Eve-only)\n            function characteristic_Voltage( service ) {\n                service.addOptionalCharacteristic( Eve.Characteristics.Voltage ); // to avoid warnings\n                floatCharacteristic( service, 'voltage', Eve.Characteristics.Voltage, null, config.topics.getVolts, {\n                    minValue: config.minVolts, maxValue: config.maxVolts\n                } );\n            }\n\n            // Eve.Characteristics.ElectricCurrent [Amperes] (Eve-only)\n            function characteristic_ElectricCurrent( service ) {\n                service.addOptionalCharacteristic( Eve.Characteristics.ElectricCurrent ); // to avoid warnings\n                floatCharacteristic( service, 'electricCurrent', Eve.Characteristics.ElectricCurrent, null, config.topics.getAmperes, 0 );\n            }\n\n            // Eve.Characteristics.TotalConsumption [kWh] (Eve-only) - optional if there is an external energy counter\n            function characteristic_TotalConsumption( service ) {\n                service.addOptionalCharacteristic( Eve.Characteristics.TotalConsumption ); // to avoid warnings\n                floatCharacteristic( service, 'totalConsumption', Eve.Characteristics.TotalConsumption, null, config.topics.getTotalConsumption, 0 );\n            }\n\n            // History for PowerConsumption (Eve-only)\n            function history_PowerConsumption( historySvc, service ) {\n                // enable mqttthing energy counter, if there is no getTotalConsumption topic\n                const energyCounter = config.topics.getTotalConsumption ? false : true;\n                var lastLogEntry = { time: 0, power: 0 };  // for energyCounter\n                // counterFile for saving 'totalConsumption' and 'resetTotal'\n                const counterFile = historyCounterFile();\n\n                function writeCounterFile() {\n                    let saveObj = { totalConsumption: state.totalConsumption, resetTotal: state.resetTotal };\n                    fs.writeFile( counterFile, JSON.stringify( saveObj ), 'utf8', function( err ) {\n                        if( err ) {\n                            log( 'Error: cannot write file to save totalConsumption' );\n                        }\n                    } );\n                }\n\n                if( energyCounter ) {\n                    // load TotalConsumption counter from counterFile\n                    fs.readFile( counterFile, 'utf8', function( err, data ) {\n                        let cnt = 0;\n                        let res = Math.floor( Date.now() / 1000 ) - 978307200;  // seconds since 01.01.2001\n                        if( err ) {\n                            log.debug( 'No data loaded for totalConsumption' );\n                        } else {\n                            cnt = JSON.parse( data ).totalConsumption;\n                            res = JSON.parse( data ).resetTotal;\n                        }\n                        service.addOptionalCharacteristic( Eve.Characteristics.TotalConsumption ); // to avoid warnings\n                        addCharacteristic( service, 'totalConsumption', Eve.Characteristics.TotalConsumption, cnt );\n                        historySvc.addOptionalCharacteristic( Eve.Characteristics.ResetTotal ); // to avoid warnings\n                        addCharacteristic( historySvc, 'resetTotal', Eve.Characteristics.ResetTotal, res, function() {\n                            state.totalConsumption = 0; // reset counter\n                            service.updateCharacteristic( Eve.Characteristics.TotalConsumption, 0 );\n                            writeCounterFile();\n                            log( \"Reset TotalConsumption to 0\" );\n                        } );\n                    } );\n                }\n\n                if( config.topics.getWatts ) {\n                    // additional MQTT subscription instead of set-callback due to correct averaging:\n                    mqttSubscribe( config.topics.getWatts, 'watts', function( topic, message ) {\n                        var logEntry = {\n                            time: Math.floor( Date.now() / 1000 ),  // seconds (UTC)\n                            power: parseFloat( message )  // fakegato-history logProperty 'power' for energy meter\n                        };\n                        if( energyCounter ) {\n                            // update Eve's Characteristic.TotalConsumption:\n                            if( lastLogEntry.time ) {\n                                // energy counter: power * timeDifference (Ws --> kWh)\n                                state.totalConsumption += lastLogEntry.power * ( logEntry.time - lastLogEntry.time ) / 1000 / 3600;\n                            }\n                            lastLogEntry.time = logEntry.time;\n                            lastLogEntry.power = logEntry.power;\n                            service.updateCharacteristic( Eve.Characteristics.TotalConsumption, state.totalConsumption );\n                            writeCounterFile();\n                        }\n                        historySvc.addEntry( logEntry );\n                    } );\n                }\n            }\n\n            // Characteristic.Active\n            function characteristic_Active( service, subIdx, subConfig ) {\n                let property_active = 'active';\n                let topic_setActive = config.topics.setActive;\n                let topic_getActive = config.topics.getActive;\n                // for usage in linked sub-services:\n                if( subIdx !== undefined && subIdx !== null && subConfig ) {\n                    property_active = property_active + '-' + subIdx;\n                    topic_setActive = subConfig.topics.setActive;\n                    topic_getActive = subConfig.topics.getActive;\n                    if( !state.activePropertyList ) {\n                        state.activePropertyList = [ property_active ];\n                    } else {\n                        state.activePropertyList.push( property_active );\n                    }\n                }\n                booleanCharacteristic( service, property_active, Characteristic.Active, topic_setActive, topic_getActive, false, function( val ) {\n                    return val ? Characteristic.Active.ACTIVE : Characteristic.Active.INACTIVE;\n                }, config.turnOffAfterms );\n            }\n\n            // Characteristic.InUse\n            function characteristic_InUse( service, subIdx, subConfig ) {\n                let property_inUse = 'inUse';\n                let topic_getInUse = config.topics.getInUse;\n                // for usage in linked sub-services:\n                if( subIdx !== undefined && subIdx !== null && subConfig ) {\n                    property_inUse = property_inUse + '-' + subIdx;\n                    topic_getInUse = subConfig.topics.getInUse;\n                    if( !state.inUsePropertyList ) {\n                        state.inUsePropertyList = [ property_inUse ];\n                    } else {\n                        state.inUsePropertyList.push( property_inUse );\n                    }\n                }\n                booleanCharacteristic( service, property_inUse, Characteristic.InUse, null, topic_getInUse, false, function( val ) {\n                    return val ? Characteristic.InUse.IN_USE : Characteristic.InUse.NOT_IN_USE;\n                } );\n            }\n\n            function linkIrrigationCharacteristics( service, valveSvc, subIdx ) {\n                service.addLinkedService( valveSvc );\n                let mainActive = service.getCharacteristic( Characteristic.Active );\n                let mainInUse = service.getCharacteristic( Characteristic.InUse );\n                let valveActive = valveSvc.getCharacteristic( Characteristic.Active );\n                let valveInUse = valveSvc.getCharacteristic( Characteristic.InUse );\n\n                // if valve is active, main service must also be active\n                // if none of the valves is active, main service should be deactivated (except with config.noAutoInactive)\n                valveActive.on( 'change', function( obj ) {\n                    if( obj.newValue == Characteristic.Active.ACTIVE && !state.active ) {\n                        state.active = true;\n                        mainActive.setValue( Characteristic.Active.ACTIVE, undefined, 'valve activated' );\n                    } else if( obj.newValue == Characteristic.Active.INACTIVE && !config.noAutoInactive ) {\n                        let mainActiveValue = false;\n                        for( let prop of state.activePropertyList ) {\n                            if( state[ prop ] ) {\n                                mainActiveValue = true;\n                                break;\n                            }\n                        }\n                        if( !mainActiveValue && state.active ) {\n                            state.active = false;\n                            mainActive.setValue( Characteristic.Active.INACTIVE, undefined, 'all valves inactive' );\n                        }\n                    }\n                } );\n\n                // if main service is set to inactive, valves should also be inactive\n                mainActive.on( 'change', function( obj ) {\n                    if( obj.newValue == Characteristic.Active.INACTIVE && state[ 'active-' + subIdx ] ) {\n                        state[ 'active-' + subIdx ] = false;\n                        valveActive.setValue( Characteristic.Active.INACTIVE, undefined, 'main off' );\n                    }\n                } );\n\n                // if valve is inUse, main service must also be inUse\n                // if none of the valves is inUse, main service should not be inUse anymore\n                valveInUse.on( 'change', function( obj ) {\n                    if( obj.newValue == Characteristic.InUse.IN_USE && !state.inUse ) {\n                        state.inUse = true;\n                        mainInUse.updateValue( Characteristic.InUse.IN_USE );\n                    } else if( obj.newValue == Characteristic.InUse.NOT_IN_USE ) {\n                        let mainInUseValue = false;\n                        for( let prop of state.inUsePropertyList ) {\n                            if( state[ prop ] ) {\n                                mainInUseValue = true;\n                                break;\n                            }\n                        }\n                        if( !mainInUseValue && state.inUse ) {\n                            state.inUse = false;\n                            mainInUse.updateValue( Characteristic.InUse.NOT_IN_USE );\n                        }\n                    }\n                } );\n            }\n\n            // Characteristic.SetDuration\n            function characteristic_SetDuration( service, subIdx, subConfig ) {\n                let property_setDuration = 'setDuration';\n                let topic_setDuration = config.topics.setDuration;\n                let topic_getDuration = config.topics.getDuration;\n                // for usage in linked sub-services:\n                if( subIdx !== undefined && subIdx !== null && subConfig ) {\n                    property_setDuration = property_setDuration + '-' + subIdx;\n                    if( subConfig.topics.setDuration ) {\n                        topic_setDuration = subConfig.topics.setDuration;\n                    }\n                    if( subConfig.topics.getDuration ) {\n                        topic_getDuration = subConfig.topics.getDuration;\n                    }\n                }\n\n                let initialValue = 1200;\n                if( config.minDuration !== undefined && initialValue < config.minDuration ) {\n                    initialValue = config.minDuration;\n                } else if( config.maxDuration !== undefined && initialValue > config.maxDuration ) {\n                    initialValue = config.maxDuration;\n                }\n\n                if( !topic_setDuration ) {\n                    /* no topic specified, but propery is still created internally */\n                    addCharacteristic( service, property_setDuration, Characteristic.SetDuration, initialValue, function() {\n                        log.debug( 'set \"' + property_setDuration + '\" to ' + state[ property_setDuration ] + 's.' );\n                    } );\n                } else {\n                    integerCharacteristic( service, property_setDuration, Characteristic.SetDuration, topic_setDuration, topic_getDuration, { initialValue } );\n                }\n                // minimum/maximum duration\n                if( config.minDuration !== undefined || config.maxDuration !== undefined ) {\n                    var charac = service.getCharacteristic( Characteristic.SetDuration );\n                    if( config.minDuration !== undefined ) {\n                        charac.props.minValue = config.minDuration;\n                    }\n                    if( config.maxDuration !== undefined ) {\n                        charac.props.maxValue = config.maxDuration;\n                    }\n                }\n            }\n\n            // Characteristic.RemainingDuration\n            function characteristic_RemainingDuration( service, subIdx, subConfig ) {\n                let property_active = 'active';\n                let property_inUse = 'inUse';\n                let property_setDuration = 'setDuration';\n                let property_durationEndTime = 'durationEndTime';\n                let topic_getRemainingDuration = config.topics.getRemainingDuration;\n                // for usage in linked sub-services:\n                if( subIdx !== undefined && subIdx !== null && subConfig ) {\n                    property_active = property_active + '-' + subIdx;\n                    property_inUse = property_inUse + '-' + subIdx;\n                    property_setDuration = property_setDuration + '-' + subIdx;\n                    property_durationEndTime = property_durationEndTime + '-' + subIdx;\n                    topic_getRemainingDuration = subConfig.topics.getRemainingDuration;\n                }\n                // Instead of saving the remaining duration, the time of the expected end is stored.\n                // This makes it easier to respond to following GET queries from HomeKit.\n                state[ property_durationEndTime ] = Math.floor( Date.now() / 1000 );\n\n                function getRemainingDuration() {\n                    let remainingDuration = state[ property_durationEndTime ] - Math.floor( Date.now() / 1000 );\n                    return ( state[ property_inUse ] && remainingDuration > 0 ) ? remainingDuration : 0;\n                }\n\n                // set up characteristic\n                let charac = service.addCharacteristic( Characteristic.RemainingDuration );\n                charac.on( 'get', function( callback ) {\n                    handleGetStateCallback( callback, getRemainingDuration() );\n                } );\n                let characActive = service.getCharacteristic( Characteristic.Active );\n                let characInUse = service.getCharacteristic( Characteristic.InUse );\n\n                // duration timer function\n                let durationTimer = null;\n\n                function timerFunc() {\n                    durationTimer = null;\n                    state[ property_active ] = false;\n                    characActive.setValue( Characteristic.Active.INACTIVE, undefined, 'time expired' );\n                    // this will also publish a MQTT message\n                }\n\n                // update durationEndTime once when 'Active' changes to ACTIVE\n                if( service.testCharacteristic( Characteristic.SetDuration ) ) {\n                    if( config.durationTimer ) {\n                        // add durationTimer (turn off timer)\n                        characInUse.on( 'change', function( obj ) {\n                            if( obj.newValue == Characteristic.InUse.IN_USE ) {\n                                state[ property_durationEndTime ] = Math.floor( Date.now() / 1000 ) + state[ property_setDuration ];\n                                durationTimer = setTimeout( timerFunc, state[ property_setDuration ] * 1000 );\n                            } else {\n                                if( durationTimer ) {\n                                    clearTimeout( durationTimer );\n                                }\n                            }\n                            charac.updateValue( getRemainingDuration() );\n                        } );\n                    } else {\n                        // device will handle the timer by itself\n                        characInUse.on( 'change', function( obj ) {\n                            if( obj.newValue == Characteristic.InUse.IN_USE ) {\n                                state[ property_durationEndTime ] = Math.floor( Date.now() / 1000 ) + state[ property_setDuration ];\n                            }\n                            charac.updateValue( getRemainingDuration() );\n                        } );\n                    }\n                } else if( config.turnOffAfterms ) {\n                    // no SetDuration Characteristic configured, but turnOffAfterms\n                    characActive.on( 'change', function( obj ) {\n                        if( obj.newValue == Characteristic.Active.ACTIVE ) {\n                            state[ property_durationEndTime ] = Math.floor( ( Date.now() + config.turnOffAfterms ) / 1000 );\n                        }\n                        charac.updateValue( getRemainingDuration() );\n                    } );\n                }\n\n                // update durationEndTime once when 'SetDuration' changes (if 'SetDuration' exists)\n                if( service.testCharacteristic( Characteristic.SetDuration ) ) {\n                    service.getCharacteristic( Characteristic.SetDuration ).on( 'change', function( obj ) {\n                        // extend or shorten duration\n                        let maxEndTime = Math.floor( Date.now() / 1000 ) + obj.newValue;\n                        let newEndTime = state[ property_durationEndTime ] + ( obj.newValue - obj.oldValue );\n                        state[ property_durationEndTime ] = ( newEndTime < maxEndTime ) ? newEndTime : maxEndTime;\n                        charac.updateValue( getRemainingDuration() );\n                        if( durationTimer ) {\n                            // update timer\n                            clearTimeout( durationTimer );\n                            durationTimer = setTimeout( timerFunc, getRemainingDuration() * 1000 );\n                        }\n                    } );\n                }\n\n                // subscribe to get topic, update remainingDuration\n                if( topic_getRemainingDuration ) {\n                    mqttSubscribe( topic_getRemainingDuration, 'remainingDuration', function( topic, message ) {\n                        let remainingDuration = parseInt( message );\n                        state[ property_durationEndTime ] = Math.floor( Date.now() / 1000 ) + remainingDuration;\n                        charac.updateValue( remainingDuration );\n                        if( durationTimer ) {\n                            // update timer\n                            clearTimeout( durationTimer );\n                            durationTimer = setTimeout( timerFunc, remainingDuration * 1000 );\n                        }\n                    } );\n                }\n            }\n\n            // Characteristic.ValveType\n            function characteristic_ValveType( service, valveType ) {\n                if( valveType === undefined || valveType === null ) {\n                    // if not specified by argument, use specification from config file\n                    if( config.valveType === 'sprinkler' ) {\n                        valveType = Characteristic.ValveType.IRRIGATION;\n                    } else if( config.valveType === 'shower' ) {\n                        valveType = Characteristic.ValveType.SHOWER_HEAD;\n                    } else if( config.valveType === 'faucet' ) {\n                        valveType = Characteristic.ValveType.WATER_FAUCET;\n                    } else {\n                        valveType = Characteristic.ValveType.GENERIC_VALVE;\n                    }\n                }\n                service.setCharacteristic( Characteristic.ValveType, valveType );\n            }\n\n            // Characteristic.ActiveIdentifier\n            function characteristic_ActiveIdentifier( service, values ) {\n                multiCharacteristic( service, 'activeIdentifier', Characteristic.ActiveIdentifier, config.topics.setActiveInput, config.topics.getActiveInput, values, 0 );\n            }\n\n            // Characteristic.VolumeSelector\n            function characteristic_RemoteKeyVolume( service ) {\n                characteristic_Remote( service, Characteristic.VolumeSelector );\n            }\n\n            // Characteristic.RemoteKey\n            function characteristic_RemoteKey( service ) {\n                characteristic_Remote( service, Characteristic.RemoteKey );\n            }\n\n            function characteristic_Remote( service, characteristic ) {\n                let values = config.remoteKeyValues;\n                if( !values ) {\n                    values = [ 'VOLUME_UP', 'VOLUME_DOWN', 'NEXT_TRACK', 'PREVIOUS_TRACK', 'UP', 'DOWN', 'LEFT', 'RIGHT',\n                        'SELECT', 'BACK', 'EXIT', 'PLAY_PAUSE', '12', '13', '14', 'INFO' ];\n                }\n                multiCharacteristic( service, 'remoteKey', characteristic, config.topics.setRemoteKey, undefined, values, null, true );\n            }\n\n            // Characteristic.FilterChangeIndication\n            function characteristic_FilterChangeIndication( service ) {\n                booleanCharacteristic( service, 'filterChangeIndication', Characteristic.FilterChangeIndication, null, config.topics.getFilterChangeIndication, false, function( val ) {\n                    return val ? Characteristic.FilterChangeIndication.CHANGE_FILTER : Characteristic.FilterChangeIndication.FILTER_OK;\n                } );\n            }\n\n            // Characteristic.FilterLifeLevel\n            function characteristic_FilterLifeLevel( service ) {\n                floatCharacteristic( service, 'filterLifeLevel', Characteristic.FilterLifeLevel, null, config.topics.getFilterLifeLevel, 100 );\n            }\n\n            // Characteristic.ResetFilterIndication\n            function characteristic_ResetFilterIndication( service ) {\n                booleanCharacteristic( service, 'resetFilterIndication', Characteristic.ResetFilterIndication, config.topics.setResetFilterIndication, null, false );\n            }\n\n            // add optional sensor characteristics\n            function addSensorOptionalCharacteristics( service ) {\n                if( config.topics.getStatusActive ) {\n                    characteristic_StatusActive( service );\n                }\n                if( config.topics.getStatusFault ) {\n                    characteristic_StatusFault( service );\n                }\n                if( config.topics.getStatusTampered ) {\n                    characteristic_StatusTampered( service );\n                }\n                if( config.topics.getStatusLowBattery ) {\n                    characteristic_StatusLowBattery( service );\n                }\n            }\n\n            // add battery characteristics\n            function addBatteryCharacteristics( service ) {\n                if( config.topics.getBatteryLevel ) {\n                    characteristic_BatteryLevel( service );\n                }\n                if( config.topics.getChargingState ) {\n                    characteristic_ChargingState( service );\n                }\n                if( config.topics.getStatusLowBattery ) {\n                    characteristic_StatusLowBattery( service );\n                }\n            }\n\n            let name = config.name;\n            let subtype = config.subtype;\n            let svcNames = config.serviceNames || {}; // custom names for multi-service accessories\n\n            let service = null; // to return a single service\n            let services = null; // if returning multiple services\n\n            //  config.type may be 'type-subtype', e.g. 'lightbulb-OnOff'\n            let configType = config.type.split( '-' )[ 0 ]; // ignore configuration subtype\n\n            if( configType == \"lightbulb\" ) {\n                service = new Service.Lightbulb( name, subtype );\n                if( config.topics.setHSV ) {\n                    characteristics_HSVLight( service );\n                } else if( config.topics.setRGB || config.topics.setRGBW || config.topics.setRGBWW ) {\n                    characteristics_RGBLight( service );\n                } else if( config.topics.setWhite ) {\n                    characteristics_WhiteLight( service );\n                } else {\n                    if( config.topics.setOn || !config.topics.setBrightness ) {\n                        characteristic_On( service );\n                    }\n                    if( config.topics.setBrightness ) {\n                        characteristic_Brightness( service );\n                    }\n                    if( config.topics.setHue ) {\n                        characteristic_Hue( service );\n                    }\n                    if( config.topics.setSaturation ) {\n                        characteristic_Saturation( service );\n                    }\n                    if( config.topics.setColorTemperature ) {\n                        characteristic_ColorTemperature( service );\n                    } else if( supportAdaptiveLighting() && config.topics.setHue && config.topics.setSaturation ) {\n                        // no color temperature topic, but support color - so add temperature for adaptive lighting\n                        characteristic_ColorTemperature_Internal( service );\n                    }\n                }\n            } else if( configType == \"switch\" ) {\n                service = new Service.Switch( name, subtype );\n                characteristic_On( service );\n                services = [ service ];\n                if( config.history ) {\n                    let historyOptions = new HistoryOptions();\n                    let historySvc = new HistoryService( 'switch', { displayName: name, log: log }, historyOptions );\n                    history_On( historySvc, service );\n                    // return history service too\n                    services.push( historySvc );\n                }\n            } else if( configType == \"outlet\" ) {\n                service = new Service.Outlet( name, subtype );\n                characteristic_On( service );\n                if( config.topics.getInUse ) {\n                    characteristic_OutletInUse( service );\n                }\n                if( config.topics.getWatts ) {\n                    characteristic_CurrentConsumption( service );\n                }\n                if( config.topics.getVolts ) {\n                    characteristic_Voltage( service );\n                }\n                if( config.topics.getAmperes ) {\n                    characteristic_ElectricCurrent( service );\n                }\n                if( config.topics.getTotalConsumption ) {\n                    characteristic_TotalConsumption( service );\n                }\n                services = [ service ];\n                if( config.history ) {\n                    let historyOptions = new HistoryOptions();\n                    let historySvc = new HistoryService( 'energy', { displayName: name, log: log }, historyOptions );\n                    history_PowerConsumption( historySvc, service );\n                    // return history service too\n                    services.push( historySvc );\n                }\n            } else if( configType == \"motionSensor\" ) {\n                service = new Service.MotionSensor( name, subtype );\n                characteristic_MotionDetected( service );\n                services = [ service ];\n                if( config.history ) {\n                    let historyOptions = new HistoryOptions( true );\n                    let historySvc = new HistoryService( 'motion', { displayName: name, log: log }, historyOptions );\n                    history_MotionDetected( historySvc, service );\n                    // return history service too\n                    services.push( historySvc );\n                }\n                addSensorOptionalCharacteristics( service );\n            } else if( configType == \"occupancySensor\" ) {\n                service = new Service.OccupancySensor( name, subtype );\n                characteristic_OccupancyDetected( service );\n                addSensorOptionalCharacteristics( service );\n            } else if( configType == \"lightSensor\" ) {\n                service = new Service.LightSensor( name, subtype );\n                characteristic_CurrentAmbientLightLevel( service );\n                addSensorOptionalCharacteristics( service );\n            } else if( configType == \"temperatureSensor\" ) {\n                service = new Service.TemperatureSensor( name, subtype );\n                characteristic_CurrentTemperature( service );\n                addSensorOptionalCharacteristics( service );\n                services = [ service ];\n                if( config.history ) {\n                    let historyOptions = new HistoryOptions();\n                    let historySvc = new HistoryService( 'weather', { displayName: name, log: log }, historyOptions );\n                    history_CurrentTemperature( historySvc );\n                    // return history service too\n                    services.push( historySvc );\n                }\n            } else if( configType == \"humiditySensor\" ) {\n                service = new Service.HumiditySensor( name, subtype );\n                characteristic_CurrentRelativeHumidity( service );\n                addSensorOptionalCharacteristics( service );\n                services = [ service ];\n                if( config.history ) {\n                    let historyOptions = new HistoryOptions();\n                    let historySvc = new HistoryService( 'weather', { displayName: name, log: log }, historyOptions );\n                    history_CurrentRelativeHumidity( historySvc );\n                    // return history service too\n                    services.push( historySvc );\n                }\n            } else if( configType == \"airPressureSensor\" ) {\n                service = new Eve.Services.AirPressureSensor( name, subtype );\n                characteristic_AirPressure( service );\n                addSensorOptionalCharacteristics( service );\n                services = [ service ];\n                if( config.history ) {\n                    let historyOptions = new HistoryOptions();\n                    let historySvc = new HistoryService( 'weather', { displayName: name, log: log }, historyOptions );\n                    history_AirPressure( historySvc );\n                    // return history service too\n                    services.push( historySvc );\n                }\n            } else if( configType == \"weatherStation\" ) {\n                service = new Service.TemperatureSensor( svcNames.temperature || name + \" Temperature\", subtype );\n                characteristic_CurrentTemperature( service );\n                addSensorOptionalCharacteristics( service );\n                services = [ service ];\n                if( config.topics.getCurrentRelativeHumidity ) {\n                    let humSvc = new Service.HumiditySensor( svcNames.humidity || name + \" Humidity\", subtype );\n                    characteristic_CurrentRelativeHumidity( humSvc );\n                    addSensorOptionalCharacteristics( humSvc );\n                    services.push( humSvc );\n                }\n                if( config.topics.getAirPressure ) {\n                    let presSvc = new Eve.Services.AirPressureSensor( svcNames.airPressure || name + \" AirPressure\", subtype );\n                    characteristic_AirPressure( presSvc );\n                    addSensorOptionalCharacteristics( presSvc );\n                    services.push( presSvc );\n                }\n                if( config.topics.getCurrentAmbientLightLevel ) {\n                    let lightSvc = new Service.LightSensor( svcNames.ambientLightLevel || name + \" Light Level\", subtype );\n                    characteristic_CurrentAmbientLightLevel( lightSvc );\n                    addSensorOptionalCharacteristics( lightSvc );\n                    services.push( lightSvc );\n                }\n                // custom service UUID for optional Eve characteristics\n                let weatherSvc = new Service( svcNames.weather || name + \" Weather\", \"D92D5391-92AF-4824-AF4A-356F25F25EA1\" );\n                let addWeatherSvc = false;\n                if( config.topics.getWeatherCondition ) {\n                    characteristic_WeatherCondition( weatherSvc );\n                    addWeatherSvc = true;\n                }\n                if( config.topics.getRain1h ) {\n                    characteristic_Rain1h( weatherSvc );\n                    addWeatherSvc = true;\n                }\n                if( config.topics.getRain24h ) {\n                    characteristic_Rain24h( weatherSvc );\n                    addWeatherSvc = true;\n                }\n                if( config.topics.getUVIndex ) {\n                    characteristic_UVIndex( weatherSvc );\n                    addWeatherSvc = true;\n                }\n                if( config.topics.getVisibility ) {\n                    characteristic_Visibility( weatherSvc );\n                    addWeatherSvc = true;\n                }\n                if( config.topics.getWindDirection ) {\n                    characteristic_WindDirection( weatherSvc );\n                    addWeatherSvc = true;\n                }\n                if( config.topics.getWindSpeed ) {\n                    characteristic_WindSpeed( weatherSvc );\n                    addWeatherSvc = true;\n                }\n                if( config.topics.getmaxWind ) {\n                    characteristic_MaximumWindSpeed( weatherSvc );\n                    addWeatherSvc = true;\n                }\n                if( config.topics.getDewPoint ) {\n                    characteristic_DewPoint( weatherSvc );\n                    addWeatherSvc = true;\n                }\n                if( addWeatherSvc ) {\n                    services.push( weatherSvc );\n                }\n                if( config.history ) {\n                    let historyOptions = new HistoryOptions();\n                    let historySvc = new HistoryService( 'weather', { displayName: name, log: log }, historyOptions );\n                    history_CurrentTemperature( historySvc );\n                    history_CurrentRelativeHumidity( historySvc );\n                    history_AirPressure( historySvc );\n                    services.push( historySvc );\n                }\n            } else if( configType == \"contactSensor\" ) {\n                service = new Service.ContactSensor( name, subtype );\n                characteristic_ContactSensorState( service );\n                addSensorOptionalCharacteristics( service );\n                services = [ service ];\n                if( config.history ) {\n                    let historyOptions = new HistoryOptions( true );\n                    let historySvc = new HistoryService( 'door', { displayName: name, log: log }, historyOptions );\n                    history_ContactSensorState( historySvc, service );\n                    // return history service too\n                    services.push( historySvc );\n                }\n            } else if( configType == \"doorbell\" ) {\n                service = new Service.Doorbell( name, subtype );\n                characteristic_ProgrammableSwitchEvent( service, 'switch', config.topics.getSwitch, config.switchValues, config.restrictSwitchValues );\n                if( config.topics.setBrightness || config.topics.getBrightness ) {\n                    characteristic_Brightness( service );\n                }\n                if( config.topics.setVolume || config.topics.getVolume ) {\n                    characteristic_Volume( service );\n                }\n                services = [ service ];\n                if( config.topics.getMotionDetected ) {\n                    // also create motion sensor\n                    let motionsvc = new Service.MotionSensor( svcNames.motion || name + '-motion', subtype );\n                    characteristic_MotionDetected( motionsvc );\n                    // return motion sensor too\n                    services.push( motionsvc );\n                }\n            } else if( configType == \"dehumidifier\" ) {\n                service = new Service.HumidifierDehumidifier( name, subtype );\n                characteristic_Active( service );\n                characteristic_CurrentRelativeHumidity( service );\n                characteristic_CurrentHumidifierDehumidifierState( service );\n                characteristic_TargetHumidifierDehumidifierState( service );\n\n                if( config.topics.setRelativeHumidityDehumidifierThreshold ) {\n                    characteristic_RelativeHumidityDehumidifierThreshold( service );\n                }\n\n                if( config.topics.getWaterLevel ) {\n                    characteristic_WaterLevel( service );\n                }\n            } else if( configType == \"statelessProgrammableSwitch\" ) {\n                if( Array.isArray( config.topics.getSwitch ) ) {\n                    service = new Service.ServiceLabel( name );\n                    characteristic_ServiceLabelNamespace( service );\n                    services = [ service ];\n                    var i = 0;\n                    for( i = 0; i < config.topics.getSwitch.length; i++ ) {\n                        let buttonTopic = config.topics.getSwitch[ i ];\n                        let switchValues = config.switchValues;\n                        if( switchValues ) {\n                            if( Array.isArray( config.switchValues[ 0 ] ) ) {\n                                if( config.switchValues.length > i ) {\n                                    switchValues = config.switchValues[ i ];\n                                } else {\n                                    // If array is not long enough, just use the first entry\n                                    switchValues = config.switchValues[ 0 ];\n                                }\n                            }\n                        }\n                        let restrictSwitchValues = config.restrictSwitchValues;\n                        if( restrictSwitchValues ) {\n                            if( Array.isArray( config.restrictSwitchValues[ 0 ] ) ) {\n                                if( config.restrictSwitchValues.length > i ) {\n                                    restrictSwitchValues = config.restrictSwitchValues[ i ];\n                                } else {\n                                    // If array is not long enough, just use the first entry\n                                    restrictSwitchValues = config.restrictSwitchValues[ 0 ];\n                                }\n                            }\n                        }\n                        let buttonSvc = new Service.StatelessProgrammableSwitch( name + \" \" + i, i + 1 );\n                        characteristic_ProgrammableSwitchEvent( buttonSvc, 'switch' + i, buttonTopic, switchValues, restrictSwitchValues );\n                        characteristic_ServiceLabelIndex( buttonSvc, i + 1 );\n                        services.push( buttonSvc );\n                    }\n                } else {\n                    service = new Service.StatelessProgrammableSwitch( name, subtype );\n                    characteristic_ProgrammableSwitchEvent( service, 'switch', config.topics.getSwitch, config.switchValues, config.restrictSwitchValues );\n                }\n            } else if( configType == \"securitySystem\" ) {\n                service = new Service.SecuritySystem( name, subtype );\n                characteristic_SecuritySystemTargetState( service );\n                characteristic_SecuritySystemCurrentState( service );\n                if( config.topics.getStatusFault ) {\n                    characteristic_StatusFault( service );\n                }\n                if( config.topics.getStatusTampered ) {\n                    characteristic_StatusTampered( service );\n                }\n                if( config.topics.getAltSensorState ) {\n                    characteristic_AltSensorState( service );\n                }\n                // todo: SecuritySystemAlarmType\n            } else if( configType == \"smokeSensor\" ) {\n                service = new Service.SmokeSensor( name, subtype );\n                characteristic_SmokeDetected( service );\n                addSensorOptionalCharacteristics( service );\n            } else if( configType == \"garageDoorOpener\" ) {\n                service = new Service.GarageDoorOpener( name, subtype );\n                characteristic_TargetDoorState( service );\n                if( config.topics.getDoorMoving ) {\n                    characteristic_DoorMoving( service );\n                } else {\n                    characteristic_CurrentDoorState( service );\n                }\n                characteristic_ObstructionDetected( service );\n                if( config.topics.setLockTargetState ) {\n                    characteristic_LockTargetState( service );\n                }\n                if( config.topics.getLockCurrentState ) {\n                    characteristic_LockCurrentState( service );\n                }\n            } else if( configType == \"lockMechanism\" ) {\n                service = new Service.LockMechanism( name, subtype );\n                if( config.topics.setLockTargetState ) {\n                    characteristic_LockTargetState( service );\n                }\n                if( config.topics.getLockCurrentState ) {\n                    characteristic_LockCurrentState( service );\n                }\n            } else if( configType == \"fan\" ) {\n                service = new Service.Fan( name, subtype );\n                if( config.topics.setOn || !config.topics.setRotationSpeed ) {\n                    characteristic_On( service );\n                }\n                if( config.topics.getRotationDirection || config.topics.setRotationDirection ) {\n                    characteristic_RotationDirection( service );\n                }\n                if( config.topics.getRotationSpeed || config.topics.setRotationSpeed ) {\n                    characteristic_RotationSpeed( service, true );\n                }\n            } else if( configType == \"leakSensor\" ) {\n                service = new Service.LeakSensor( name, subtype );\n                characteristic_LeakDetected( service );\n                if( config.topics.setWaterLevel || config.topics.getWaterLevel ) {\n                    characteristic_WaterLevel( service );\n                }\n                addSensorOptionalCharacteristics( service );\n            } else if( configType == \"microphone\" ) {\n                service = new Service.Microphone( name, subtype );\n                characteristic_Mute( service );\n                if( config.topics.setVolume || config.topics.getVolume ) {\n                    characteristic_Volume( service );\n                }\n            } else if( configType == \"speaker\" ) {\n                service = new Service.Speaker( name, subtype );\n                characteristic_Mute( service );\n                if( config.topics.setVolume || config.topics.getVolume ) {\n                    characteristic_Volume( service );\n                }\n            } else if( configType == \"windowCovering\" ) {\n                service = new Service.WindowCovering( name, subtype );\n                characteristic_CurrentPosition( service );\n                characteristic_TargetPosition( service );\n                characteristic_PositionState( service );\n                if( config.topics.setHoldPosition ) {\n                    characteristic_HoldPosition( service );\n                }\n                if( config.topics.getTargetHorizontalTiltAngle || config.topics.setTargetHorizontalTiltAngle ) {\n                    Characteristic_TargetHorizontalTiltAngle( service );\n                }\n                if( config.topics.getTargetVerticalTiltAngle || config.topics.setTargetVerticalTiltAngle ) {\n                    Characteristic_TargetVerticalTiltAngle( service );\n                }\n                if( config.topics.getCurrentHorizontalTiltAngle ) {\n                    Characteristic_CurrentHorizontalTiltAngle( service );\n                }\n                if( config.topics.getCurrentVerticalTiltAngle ) {\n                    Characteristic_CurrentVerticalTiltAngle( service );\n                }\n                if( config.topics.getObstructionDetected ) {\n                    characteristic_ObstructionDetected( service );\n                }\n            } else if( configType == \"window\" ) {\n                service = new Service.Window( name, subtype );\n                characteristic_CurrentPosition( service );\n                characteristic_TargetPosition( service );\n                characteristic_PositionState( service );\n                if( config.topics.setHoldPosition ) {\n                    characteristic_HoldPosition( service );\n                }\n                if( config.topics.getObstructionDetected ) {\n                    characteristic_ObstructionDetected( service );\n                }\n            } else if( configType == \"door\" ) {\n                service = new Service.Door( name, subtype );\n                characteristic_CurrentPosition( service );\n                characteristic_TargetPosition( service );\n                characteristic_PositionState( service );\n                if( config.topics.setHoldPosition ) {\n                    characteristic_HoldPosition( service );\n                }\n                if( config.topics.getObstructionDetected ) {\n                    characteristic_ObstructionDetected( service );\n                }\n            } else if( configType == \"airQualitySensor\" ) {\n                service = new Service.AirQualitySensor( svcNames.airQuality || name, subtype );\n                characteristic_AirQuality( service );\n                addSensorOptionalCharacteristics( service );\n                if( config.topics.getCarbonDioxideLevel ) {\n                    characteristic_CarbonDioxideLevel( service );\n                }\n                if( config.topics.getPM10Density ) {\n                    characteristic_PM10Density( service );\n                }\n                if( config.topics.getPM2_5Density ) {\n                    characteristic_PM2_5Density( service );\n                }\n                if( config.topics.getOzoneDensity ) {\n                    characteristic_OzoneDensity( service );\n                }\n                if( config.topics.getNitrogenDioxideDensity ) {\n                    characteristic_NitrogenDioxideDensity( service );\n                }\n                if( config.topics.getSulphurDioxideDensity ) {\n                    characteristic_SulphurDioxideDensity( service );\n                }\n                if( config.topics.getVOCDensity ) {\n                    characteristic_VOCDensity( service );\n                }\n                if( config.topics.getCarbonMonoxideLevel ) {\n                    characteristic_CarbonMonoxideLevel( service );\n                }\n                services = [ service ];\n                if( config.topics.getCurrentTemperature ) {\n                    let tempSvc = new Service.TemperatureSensor( svcNames.temperature || name + \"-Temperature\", subtype );\n                    characteristic_CurrentTemperature( tempSvc );\n                    characteristic_TemperatureDisplayUnits( tempSvc );\n                    addSensorOptionalCharacteristics( tempSvc );\n                    services.push( tempSvc );\n                }\n                if( config.topics.getCurrentRelativeHumidity ) {\n                    let humSvc = new Service.HumiditySensor( svcNames.humidity || name + \"-Humidity\", subtype );\n                    characteristic_CurrentRelativeHumidity( humSvc );\n                    addSensorOptionalCharacteristics( humSvc );\n                    services.push( humSvc );\n                }\n                if( config.history && config.room2 ) {\n                    let historyOptions = new HistoryOptions();\n                    let historySvc = new HistoryService( 'room2', { displayName: name, log: log }, historyOptions );\n                    history_VOCDensity( historySvc );\n                    history_CurrentTemperature( historySvc );\n                    history_CurrentRelativeHumidity( historySvc );\n                    services.push( historySvc );\n                } else if( config.history ) {\n                    let historyOptions = new HistoryOptions();\n                    let historySvc = new HistoryService( 'room', { displayName: name, log: log }, historyOptions );\n                    if( config.topics.getAirQualityPPM ) {\n                        characteristic_AirQualityPPM( service );\n                    }\n                    history_AirQualityPPM( historySvc );\n                    history_CurrentTemperature( historySvc );\n                    history_CurrentRelativeHumidity( historySvc );\n                    services.push( historySvc );\n                }\n            } else if( configType == 'carbonDioxideSensor' ) {\n                service = new Service.CarbonDioxideSensor( name, subtype );\n                characteristic_CarbonDioxideDetected( service );\n                addSensorOptionalCharacteristics( service );\n                if( config.topics.getCarbonDioxideLevel ) {\n                    characteristic_CarbonDioxideLevel( service );\n                }\n                if( config.topics.getCarbonDioxidePeakLevel ) {\n                    characteristic_CarbonDioxidePeakLevel( service );\n                }\n            } else if ( configType == 'carbonMonoxideSensor' ) {\n                service = new Service.CarbonMonoxideSensor(name, subtype);\n                characteristic_CarbonMonoxideDetected(service);\n                addSensorOptionalCharacteristics(service);\n                if (config.topics.getcarbonMonoxideLevel) {\n                    characteristic_carbonMonoxideLevel(service);\n                }\n                if (config.topics.getcarbonMonoxidePeakLevel) {\n                    characteristic_carbonMonoxidePeakLevel(service);\n                }\n            } else if( configType == 'valve' ) {\n                service = new Service.Valve( name, subtype );\n                characteristic_ValveType( service );\n                characteristic_Active( service );\n                characteristic_InUse( service );\n                if( config.topics.setDuration || config.durationTimer ) {\n                    characteristic_SetDuration( service );\n                    characteristic_RemainingDuration( service );\n                } else if( config.topics.getRemainingDuration || config.turnOffAfterms ) {\n                    characteristic_RemainingDuration( service );\n                }\n                addSensorOptionalCharacteristics( service );\n            } else if( configType == 'thermostat' ) {\n                service = new Service.Thermostat( name, subtype );\n                characteristic_Active( service );\n                characteristic_CurrentHeatingCoolingState( service );\n                characteristic_TargetHeatingCoolingState( service );\n                characteristic_CurrentTemperature( service );\n                characteristic_TargetTemperature( service );\n                characteristic_TemperatureDisplayUnits( service );\n                if( config.topics.getCurrentRelativeHumidity ) {\n                    characteristic_CurrentRelativeHumidity( service );\n                }\n                if( config.topics.getTargetRelativeHumidity || config.topics.setTargetRelativeHumidity ) {\n                    characteristic_TargetRelativeHumidity( service );\n                }\n                if( config.topics.getCoolingThresholdTemperature || config.topics.setCoolingThresholdTemperature ) {\n                    characteristic_CoolingThresholdTemperature( service );\n                }\n                if( config.topics.getHeatingThresholdTemperature || config.topics.setHeatingThresholdTemperature ) {\n                    characteristic_HeatingThresholdTemperature( service );\n                }\n                if( config.topics.getStatusFault ) {\n                    characteristic_StatusFault( service );\n                }\n            } else if( configType == \"heaterCooler\" ) {\n                service = new Service.HeaterCooler( name, subtype );\n                characteristic_Active( service );\n                characteristic_CurrentHeaterCoolerState( service );\n                characteristic_TargetHeaterCoolerState( service );\n                characteristic_CurrentTemperature( service );\n                if( config.topics.setLockPhysicalControls || config.topics.getLockPhysicalControls ) {\n                    characteristic_LockPhysicalControls( service );\n                }\n                if( config.topics.getSwingMode || config.topics.setSwingMode ) {\n                    characteristic_SwingMode( service );\n                }\n                if( config.topics.getCoolingThresholdTemperature || config.topics.setCoolingThresholdTemperature ) {\n                    characteristic_CoolingThresholdTemperature( service );\n                }\n                if( config.topics.getHeatingThresholdTemperature || config.topics.setHeatingThresholdTemperature ) {\n                    characteristic_HeatingThresholdTemperature( service );\n                }\n                if( config.topics.getTemperatureDisplayUnits || config.topics.setTemperatureDisplayUnits ) {\n                    characteristic_TemperatureDisplayUnits( service );\n                }\n                if( config.topics.getRotationSpeed || config.topics.setRotationSpeed ) {\n                    characteristic_RotationSpeed( service );\n                }\n                if( config.topics.getStatusFault ) {\n                    characteristic_StatusFault( service );\n                }\n            } else if( configType == 'television' ) {\n                service = new Service.Television( name, subtype );\n                service.isPrimaryService = true;\n                characteristic_Active( service );\n                service.setCharacteristic( Characteristic.ActiveIdentifier, 0 );\n                service.setCharacteristic( Characteristic.ConfiguredName, name );\n                service.setCharacteristic( Characteristic.SleepDiscoveryMode, Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE );\n                // service.setCharacteristic(Characteristic.Brightness, XXX);  // no impact?\n                // service.setCharacteristic(Characteristic.ClosedCaptions, XXX);  // no impact?\n                // service.setCharacteristic(Characteristic.CurrentMediaState, XXX);  // no impact?\n                // service.setCharacteristic(Characteristic.TargetMediaState, XXX);  // no impact?\n                // service.setCharacteristic(Characteristic.PictureMode, XXX);  // no impact?\n                // service.addCharacteristic(Characteristic.PowerModeSelection);  // this would add a button in TV settings\n\n                var speakerService = new Service.TelevisionSpeaker(\n                    'Volume',\n                    'volumeService'\n                );\n                speakerService\n                    .setCharacteristic( Characteristic.Active, Characteristic.Active.ACTIVE )\n                    .setCharacteristic(\n                        Characteristic.VolumeControlType,\n                        Characteristic.VolumeControlType.ABSOLUTE\n                    );\n\n                characteristic_RemoteKey( service );\n                characteristic_RemoteKeyVolume( speakerService );\n\n                services = [ service, speakerService ];\n\n                if( config.inputs ) {\n                    var inputValues = [ 'NONE' ];   // MQTT values for ActiveIdentifier\n                    var displayOrderTlvArray = [];  // for specific order instead of default alphabetical ordering\n                    config.inputs.forEach( function( input, index ) {\n                        let inputId = index + 1;\n                        let inputName = input.name || 'Input ' + inputId;\n                        let inputSvc = new Service.InputSource( inputName, inputId );\n                        inputSvc.isHiddenService = true;  // not sure if necessary\n                        service.addLinkedService( inputSvc );  // inputSvc must be linked to main service\n                        inputSvc.setCharacteristic( Characteristic.Identifier, inputId );\n                        inputSvc.setCharacteristic( Characteristic.ConfiguredName, inputName );\n                        inputSvc.setCharacteristic( Characteristic.IsConfigured, Characteristic.IsConfigured.CONFIGURED );  // necessary for input to appear\n                        inputSvc.setCharacteristic( Characteristic.InputDeviceType, Characteristic.InputDeviceType.OTHER ); // no impact?\n                        inputSvc.setCharacteristic( Characteristic.InputSourceType, Characteristic.InputSourceType.OTHER ); // no impact?\n                        var visibilityStateProperty = 'input' + inputId + '-visible';\n                        addCharacteristic( inputSvc, visibilityStateProperty, Characteristic.TargetVisibilityState, Characteristic.TargetVisibilityState.SHOWN, function() {\n                            // change CurrentVisibilityState when TargetVisibilityState changes\n                            inputSvc.setCharacteristic( Characteristic.CurrentVisibilityState, state[ visibilityStateProperty ] );\n                        } );\n                        inputValues.push( input.value || inputId );\n                        displayOrderTlvArray.push( 1, 1, inputId );  // type = 1 (\"Identifier\"), length = 1 Byte, Identifier value\n                        services.push( inputSvc );\n                    } );\n                    characteristic_ActiveIdentifier( service, inputValues );  // for selecting inputs\n                    var displayOrderTlv = Buffer.from( displayOrderTlvArray ).toString( 'base64' );\n                    service.setCharacteristic( Characteristic.DisplayOrder, displayOrderTlv );\n                }\n            } else if( config.type == 'irrigationSystem' ) {\n                service = new Service.IrrigationSystem( name, subtype );\n                service.isPrimaryService = true;\n                if( !config.topics ) {\n                    config.topics = {};\n                }\n                characteristic_Active( service );\n                characteristic_InUse( service );\n                service.setCharacteristic( Characteristic.ProgramMode, Characteristic.ProgramMode.NO_PROGRAM_SCHEDULED );\n                if( config.topics.getStatusFault ) {\n                    characteristic_StatusFault( service );\n                }\n\n                services = [ service ];\n\n                if( config.zones ) {\n                    let serviceLabel = new Service.ServiceLabel();\n                    serviceLabel.setCharacteristic( Characteristic.ServiceLabelNamespace, Characteristic.ServiceLabelNamespace.ARABIC_NUMERALS );\n                    services.push( serviceLabel );\n                    config.zones.forEach( function( zone, index ) {\n                        let zoneId = index + 1;\n                        let zoneName = zone.name || ''; // default name doesn't seem to work\n                        let valveSvc = new Service.Valve( zoneName, zoneId );\n                        characteristic_ValveType( valveSvc, Characteristic.ValveType.IRRIGATION );\n                        characteristic_ServiceLabelIndex( valveSvc, zoneId );\n                        characteristic_Active( valveSvc, zoneId, zone );\n                        characteristic_InUse( valveSvc, zoneId, zone );\n                        characteristic_SetDuration( valveSvc, zoneId, zone );\n                        characteristic_RemainingDuration( valveSvc, zoneId, zone );\n                        valveSvc.setCharacteristic( Characteristic.IsConfigured, Characteristic.IsConfigured.CONFIGURED );\n                        if( zone.topics.getStatusFault ) {\n                            characteristic_StatusFault( valveSvc );\n                        }\n                        linkIrrigationCharacteristics( service, valveSvc, zoneId );  // valveSvc must be linked to main service\n                        services.push( valveSvc );\n                    } );\n                }\n            } else if( configType == \"airPurifier\" ) {\n                service = new Service.AirPurifier( name, subtype );\n                characteristic_Active( service );\n                characteristic_CurrentAirPurifierState( service );\n                characteristic_TargetAirPurifierState( service );\n                if( config.topics.getRotationSpeed || config.topics.setRotationSpeed ) {\n                    characteristic_RotationSpeed( service );\n                }\n                if( config.topics.getSwingMode || config.topics.setSwingMode ) {\n                    characteristic_SwingMode( service );\n                }\n                if( config.topics.setLockPhysicalControls || config.topics.getLockPhysicalControls ) {\n                    characteristic_LockPhysicalControls( service );\n                }\n                services = [ service ];\n                if( config.topics.getFilterChangeIndication || config.topics.getFilterLifeLevel || config.topics.setResetFilterIndication ) {\n                    let filterSvc = new Service.FilterMaintenance( svcNames.filter || name + \"-Filter\", subtype );\n                    service.addLinkedService( filterSvc );\n                    characteristic_FilterChangeIndication( filterSvc ); // required\n                    if( config.topics.getFilterLifeLevel ) {\n                        characteristic_FilterLifeLevel( filterSvc );\n                    }\n                    if( config.topics.setResetFilterIndication ) {\n                        characteristic_ResetFilterIndication( filterSvc );\n                    }\n                    services.push( filterSvc );\n                }\n            } else if( configType == \"fanv2\" ) {\n                service = new Service.Fanv2(name, subtype);\n                characteristic_Active(service);\n                if(config.getCurrentFanState) {\n                    characteristic_CurrentFanState(service);\n                }\n                if(config.topics.setTargetFanState || config.topics.getTargetFanState) {\n                    characteristic_TargetFanState(service);\n                }\n                if (config.topics.setLockPhysicalControls || config.topics.getLockPhysicalControls) {\n                    characteristic_LockPhysicalControls(service);\n                }\n                if (config.topics.getRotationDirection || config.topics.setRotationDirection) {\n                    characteristic_RotationDirection(service);\n                }\n                if (config.topics.getRotationSpeed || config.topics.setRotationSpeed) {\n                    characteristic_RotationSpeed(service);\n                }\n\n                if (config.topics.getSwingMode || config.topics.setSwingMode) {\n                    characteristic_SwingMode(service);\n                }\n                services = [service];\n            } else if( configType == 'battery' ) {\n                service = new Service.Battery( name );\n                addBatteryCharacteristics( service );\n            } else {\n                log( \"ERROR: Unrecognized type: \" + configType );\n            }\n\n            if( service ) {\n                if( config.topics.getName ) {\n                    characteristic_Name( service );\n                }\n\n                if( config.topics.getOnline ) {\n                    state_Online();\n                }\n\n                // name override\n                if( config.nameOverride ) {\n                    service.setCharacteristic( Characteristic.ConfiguredName, config.nameOverride );\n                }\n            }\n\n            // always use services array\n            if( !services ) {\n                if( service ) {\n                    services = [ service ];\n                } else {\n                    log( 'Error: No service(s) created for ' + name );\n                    return;\n                }\n            }\n\n            // optional battery service\n            if( configType !== 'battery' ) {\n                if( config.topics.getBatteryLevel || config.topics.getChargingState ||\n                    ( config.topics.getStatusLowBattery && !service.testCharacteristic( Characteristic.StatusLowBattery ) ) ) {\n                    // also create battery service\n                    let batsvc = new Service.Battery( name + '-battery' );\n                    addBatteryCharacteristics( batsvc );\n                    services.push( batsvc );\n                }\n            }\n\n            return services;\n        }\n\n        let services = null;\n\n        if( accessoryConfig.type === \"custom\" && accessoryConfig.services ) {\n            // multi-service/custom configuration...\n            services = [];\n            for( let svcCfg of accessoryConfig.services ) {\n                let config = { ...accessoryConfig, ...svcCfg };\n                if( !config.hasOwnProperty( 'subtype' ) ) {\n                    config.subtype = config.name;\n                }\n                services = [ ...services, ...configToServices( config ) ];\n            }\n        } else {\n            // single accessory\n            services = configToServices( accessoryConfig );\n        }\n\n        // accessory information service\n        services.push( makeAccessoryInformationService() );\n\n        // start-up publishing\n        if( accessoryConfig.startPub ) {\n            if( Array.isArray( accessoryConfig.startPub ) ) {\n                // new format - [ { topic: x, message: y }, ... ]\n                for( let entry of accessoryConfig.startPub ) {\n                    if( entry.topic ) {\n                        mqttPublish( entry.topic, 'startPub', entry.message || '' );\n                    }\n                }\n            } else {\n                // old format - object of topic->message\n                for( let topic in accessoryConfig.startPub ) {\n                    if( accessoryConfig.startPub.hasOwnProperty( topic ) ) {\n                        let msg = accessoryConfig.startPub[ topic ];\n                        mqttPublish( topic, 'startPub', msg );\n                    }\n                }\n            }\n        }\n\n        return services;\n    }\n\n    // The service\n    var theServices = null;\n    try {\n        theServices = createServices();\n    } catch( ex ) {\n        log.error( 'Exception while creating services: ' + ex );\n        log( ex.stack );\n    }\n\n    // Our accessory instance\n    var thing = {};\n\n    // Return services\n    thing.getServices = function() {\n        return theServices || [];\n    };\n\n    // Return controllers\n    thing.getControllers = function() {\n        return controllers;\n    };\n\n    return thing;\n}\n\n// Homebridge Entry point\nmodule.exports = function( homebridge ) {\n    Service = homebridge.hap.Service;\n    Characteristic = homebridge.hap.Characteristic;\n    Eve = new EveHomeKitTypes( homebridge );\n    HistoryService = fakegatoHistory( homebridge );\n    homebridgePath = homebridge.user.storagePath();\n\n    homebridge.registerAccessory( \"homebridge-mqttthing\", \"mqttthing\", makeThing );\n};\n"]}