{"version":3,"sources":["jsdelivr-header.js","/npm/homebridge-dyson-link@2.5.6/index.js"],"names":[],"mappings":"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","file":"/npm/homebridge-dyson-link@2.5.6/index.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/homebridge-dyson-link@2.5.6/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","\"use strict\";\n\nconst https = require('https');\nconst crypto = require('crypto');\n\nconst DysonLinkAccessoryModule = require(\"./DysonLinkAccessory\");\nconst DysonLinkDevice = require(\"./DysonLinkDevice\").DysonLinkDevice;\nconst DysonLinkAccessory = DysonLinkAccessoryModule.DysonLinkAccessory;\n\nvar Accessory, Service, Characteristic, UUIDGen;\n\nmodule.exports = function(homebridge) {\n    console.log(\"homebridge API version: \" + homebridge.version);\n\n    // Accessory must be created from PlatformAccessory Constructor\n    Accessory = homebridge.platformAccessory;\n\n    // Service and Characteristic are from hap-nodejs\n    Service = homebridge.hap.Service;\n    Characteristic = homebridge.hap.Characteristic;\n    UUIDGen = homebridge.hap.uuid;\n\n    DysonLinkAccessoryModule.setHomebridge(homebridge);\n\n    // For platform plugin to be considered as dynamic platform plugin,\n    // registerPlatform(pluginName, platformName, constructor, dynamic), dynamic must be true\n    homebridge.registerPlatform(\"homebridge-dyson-link\", \"DysonPlatform\", DysonPlatform, true);\n}\n\nclass DysonPlatform {\n    constructor(log, config, api) {\n        this.log = log;\n        this.config = config;\n        this.accessories = [];\n\n        if (api) {\n            // Save the API object as plugin needs to register new accessory via this object.\n            this.api = api;\n            var platform = this;\n            // Listen to event \"didFinishLaunching\", this means homebridge already finished loading cached accessories\n            // Platform Plugin should only register new accessory that doesn't exist in homebridge after this event.\n            // Or start discover new accessories\n            this.api.on('didFinishLaunching', () => {\n                platform.log(\"Finished launching. Start to create accessory from config\");\n                // Check if the accessories is null as this may be called from second instance of homebrdige too\n                if (this.config && this.config.accessories) {\n                    let accountPassword = this.config.password || process.env.DYSON_PASSWORD;\n                    let accountEmail = this.config.email || process.env.DYSON_EMAIL;\n                    this.getDevicesFromAccount(accountEmail, accountPassword, config.country, (accountDevices) => {\n                        this.config.accessories.forEach((accessory) => {\n                            let nightModeVisible = accessory.nightModeVisible;\n                            if(nightModeVisible == null || nightModeVisible == undefined) {\n                                platform.log.debug(\"no night mode visible value, default to true\");\n                                nightModeVisible = true;\n                            }\n                            let focusModeVisible = accessory.focusModeVisible;\n                            if(focusModeVisible == null || focusModeVisible == undefined) {\n                                platform.log.debug(\"no focus mode visible value, default to true\");\n                                focusModeVisible = true;\n                            }\n                            let autoModeVisible = accessory.autoModeVisible;\n                            if(autoModeVisible == null || autoModeVisible == undefined) {\n                                platform.log.debug(\"no auto mode visible value, default to true\");\n                                autoModeVisible = true;\n                            }\n                            let deviceInfo = accountDevices[accessory.serialNumber];\n                            var password = ''\n                            if (deviceInfo) {\n                                platform.log(\"Use device password from account\");\n                                password = deviceInfo.password;\n                                accessory.serialNumber = 'DYSON-'+accessory.serialNumber+'-'+deviceInfo.ProductType;\n                            }\n                            else if (accessory.password) {\n                                platform.log(\"Use device password from config file\");\n                                password = crypto.createHash('sha512').update(accessory.password, \"utf8\").digest(\"base64\");\n                            }\n                            else {\n                                platform.log.error(\"Missing password for device with serial number \" + accessory.serialNumber + \", devices found on your account: \" + Object.keys(accountDevices).join(\", \"));\n                                return;\n                            }\n                            platform.log(accessory.displayName + \" IP:\" + accessory.ip + \" Serial Number:\" + accessory.serialNumber);\n                            let device = new DysonLinkDevice(accessory.displayName, accessory.ip, accessory.serialNumber, password, platform.log);\n                            if (device.valid) {\n                                platform.log(\"Device serial number format valids\");\n                                let uuid = UUIDGen.generate(accessory.serialNumber);\n                                // Check if the accessory got cached\n                                let cachedAccessory = platform.accessories.find((item) => item.UUID === uuid);\n                                if (!cachedAccessory) {\n                                    platform.log(\"Device not cached. Create a new one\");\n                                    let dysonAccessory = new Accessory(accessory.displayName, uuid);\n                                    new DysonLinkAccessory(accessory.displayName, device, dysonAccessory, platform.log, nightModeVisible, focusModeVisible, autoModeVisible);\n                                    platform.api.registerPlatformAccessories(\"homebridge-dyson-link\", \"DysonPlatform\", [dysonAccessory]);\n                                    platform.accessories.push(accessory);\n                                } else {\n                                    platform.log(\"Device cached. Try to update this\");\n                                    cachedAccessory.displayName = accessory.displayName;\n                                    new DysonLinkAccessory(accessory.displayName, device, cachedAccessory, platform.log, nightModeVisible, focusModeVisible, autoModeVisible);\n                                    platform.api.updatePlatformAccessories([cachedAccessory]);\n                                }\n                            }\n                        });\n                    });\n\n                }\n                else{\n                    platform.log.error(\"Unable to find config or accessories\");\n                }\n            });\n        }\n\n    }\n\n    configureAccessory(accessory) {\n        this.log(accessory.displayName, \"Configure Accessory\");\n        accessory.reachable = true;\n        accessory.on('identify', (paired, callback) => {\n            this.log(accessory.displayName, \"Identify!!!\");\n            callback();\n        });\n\n        this.accessories.push(accessory);\n    }\n\n    getDevicesFromAccount(email, password, country, callback) {\n        if (!email || !password) {\n            this.log(\"Dyson email/pass not found, v2 devices may not work\")\n            callback({});\n            return;\n        }\n        // Adapted from: https://github.com/CharlesBlonde/libpurecoollink/blob/master/libpurecoollink/utils.py\n        const decryptPassword = (encryptedPassword) => {\n            let key = Uint8Array.from(Array(32), (val, index) => index + 1);\n            let init_vector = new Uint8Array(16);\n            var decipher = crypto.createDecipheriv('aes-256-cbc', key, init_vector);\n            var decryptedPassword = decipher.update(encryptedPassword, 'base64', 'utf8');\n            decryptedPassword = decryptedPassword + decipher.final('utf8');\n            return decryptedPassword\n        };\n\n        if (!country) {\n            country = \"US\"\n        }\n\n        let DYSON_API_URL = \"appapi.cp.dyson.com\";\n        if (country == \"CN\"){\n            DYSON_API_URL = \"appapi.cp.dyson.cn\"\n            this.log.info(\"Country code is CN. Changed to use CN server -\" + DYSON_API_URL);\n        }\n\n        let postData = {\n            Email: email,\n            Password: password\n        };\n        let postBody = JSON.stringify(postData);\n\n\n        var options = {\n            hostname: DYSON_API_URL,\n            port: 443,\n            path: '/v1/userregistration/authenticate?country=' + country,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'application/json',\n                'Content-Length': postBody.length,\n                'User-Agent': 'android client'\n            },\n            rejectUnauthorized: false\n        };\n        // Initial request with email/pass to get authorization tokens of Account and Password\n        var req = https.request(options, (res) => {\n            var data = \"\";\n            res.setEncoding('utf-8');\n            res.on('data', (chunk) => data += chunk);\n            res.on('end', () => {\n                if (!data) {\n                    this.log.error(\"Could not login to Dyson\")\n                    callback({});\n                    return;\n                }\n                let credentials = JSON.parse(data);\n                let account = credentials.Account;\n                let password = credentials.Password;\n                let auth = 'Basic ' + Buffer.from(account + ':' + password).toString('base64');\n\n                var options = {\n                    hostname: DYSON_API_URL,\n                    port: 443,\n                    path: '/v2/provisioningservice/manifest',\n                    headers: {\n                        \"Authorization\": auth,\n                        'User-Agent': 'android client'\n                    },\n                    rejectUnauthorized: false\n                };\n                // Request devices in user's account to get local credentials\n                var req = https.get(options, (res) => {\n                    var data = \"\";\n                    res.setEncoding('utf-8');\n                    res.on('data', (chunk) => data += chunk);\n                    res.on('end', () => {\n                        if (!data || data.length == 0) {\n                            this.log.error(\"Could not login to Dyson\");\n                            callback({});\n                            return;\n                        }\n                        let devices = JSON.parse(data);\n                        var devicesBySerial = {};\n                        devices.forEach((device) => {\n                            if (device.LocalCredentials) {\n                                let decrypted = JSON.parse(decryptPassword(device.LocalCredentials));\n                                device.password = decrypted.apPasswordHash;\n                                devicesBySerial[device.Serial] = device\n                            }\n                        });\n                        callback(devicesBySerial);\n                    });\n                });\n                req.on('error', function(err) {\n                    this.log.error(\"Error logging in, check Dyson email, password, and country - \"+err);\n                });\n                req.end();\n            });\n        });\n        req.on('error', function(err) {\n            this.log.error(\"Error logging in, check Dyson email, password, and country - \"+err);\n        });\n        req.write(postBody);\n        req.end();\n    }\n}\n"]}