netatmo.js
File `netatmo.js` has 579 lines of code (exceeds 250 allowed). Consider refactoring./* MagicMirror² * Module: MMM-Netatmo * * By Christopher Fenner https://github.com/CFenner * MIT Licensed. *//* global Module */`` has 30 functions (exceeds 20 allowed). Consider refactoring.Module.register('netatmo', { // default config defaults: { initialDelay: 0, updateInterval: 3, // every 3 minutes, refresh interval on netatmo is 10 minutes animationSpeed: 1000, design: 'classic', // or bubbles horizontal: true, lastMessageThreshold: 600, // in seconds (10 minutes) showLastMessage: true, showBattery: true, showRadio: true, showWiFi: true, showTrend: true, showMeasurementIcon: true, showMeasurementLabel: true, showStationName: true, showModuleNameOnTop: false, apiBase: 'api.netatmo.com', authEndpoint: '/oauth2/token', dataEndpoint: '/api/getstationsdata', fontClassModuleName: 'xsmall', fontClassPrimary: 'large', fontClassSecondary: 'xsmall', fontClassMeasurement: 'xsmall', thresholdCO2Average: 800, thresholdCO2Bad: 1800, unitOfMeasurement: '', unitOfMeasurementPressure: '', unitOfMeasurementWind: '', mockData: false, }, notifications: { AUTH: 'NETATMO_AUTH', AUTH_RESPONSE: 'NETATMO_AUTH_RESPONSE', DATA: 'NETATMO_DATA', DATA_RESPONSE: 'NETATMO_DATA_RESPONSE', }, moduleType: { MAIN: 'NAMain', INDOOR: 'NAModule4', OUTDOOR: 'NAModule1', RAIN: 'NAModule3', WIND: 'NAModule2', }, measurement: { CO2: 'CO2', HUMIDITY: 'Humidity', TEMPERATURE: 'Temperature', TEMPERATURE_TREND: 'temp_trend', PRESSURE: 'Pressure', PRESSURE_TREND: 'pressure_trend', NOISE: 'Noise', WIND_STRENGTH: 'WindStrength', WIND_ANGLE: 'WindAngle', GUST_STRENGTH: 'GustStrength', GUST_ANGLE: 'GustAngle', RAIN: 'Rain', RAIN_PER_HOUR: 'sum_rain_1', RAIN_PER_DAY: 'sum_rain_24', }, // init method start () { Log.info(`Starting module: ${this.name}`) this.loaded = false this.moduleList = [] // get a new token at start-up. setTimeout(() => { // best way is using initialize at start and if auth OK --> fetch data this.sendSocketNotification('INIT', this.config) }, this.config.initialDelay * 1000) // set auto-update setInterval(() => { this.sendSocketNotification(this.notifications.DATA) }, this.config.updateInterval * 60 * 1000 + this.config.initialDelay * 1000) }, updateUnitOfMeasurements (userPreferences) {Similar blocks of code found in 3 locations. Consider refactoring. if (this.config.unitOfMeasurement === '') { this.config.unitOfMeasurement = this.convertNetatmoUnit(userPreferences.unit) console.log('Using user-preferred unit of measurement for temp/rain values %o', this.config.unitOfMeasurement) }Similar blocks of code found in 3 locations. Consider refactoring. if (this.config.unitOfMeasurementPressure === '') { this.config.unitOfMeasurementPressure = this.convertNetatmoPressureUnit(userPreferences.pressureunit) console.log('Using user-preferred unit of measurement for pressure values %o', this.config.unitOfMeasurementPressure) }Similar blocks of code found in 3 locations. Consider refactoring. if (this.config.unitOfMeasurementWind === '') { this.config.unitOfMeasurementWind = this.convertNetatmoWindUnit(userPreferences.windunit) console.log('Using user-preferred unit of measurement for wind values %o', this.config.unitOfMeasurementWind) } },Function `updateModuleList` has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring. updateModuleList (stationList) { let moduleList = [] for (const station of stationList) { moduleList.push(this.getModule(station, station.home_name)) station.modules.forEach(function (module) { moduleList.push(this.getModule(module, station.home_name)) }.bind(this)) if (station.reachable) { this.lastUpdate = station.dashboard_data.time_utc } } this.loaded = true if (JSON.stringify(this.moduleList) === JSON.stringify(moduleList)) { return } // reorder modules if (this.config.moduleOrder && this.config.moduleOrder.length > 0) { const reorderedModuleList = [] for (const moduleName of this.config.moduleOrder) { for (const module of moduleList) { if (module.name === moduleName) { reorderedModuleList.push(module) } } } moduleList = reorderedModuleList } this.moduleList = moduleList },Function `getModule` has a Cognitive Complexity of 50 (exceeds 5 allowed). Consider refactoring.
Function `getModule` has 119 lines of code (exceeds 25 allowed). Consider refactoring. getModule (module, stationName) { const result = {} result.name = module.module_name if (this.config.showStationName) { result.name = `${stationName} - ${result.name}` } result.measurementList = [] if (!module.reachable) { let measurement = '' if (module.type === this.moduleType.MAIN) { measurement = 'wifi' } else { measurement = 'radio' } result.measurementList.push({ name: measurement, value: this.getValue(measurement, 0), unit: this.getUnit(measurement), icon: `${this.getIcon(measurement, 0)} flash red`, label: this.translate(measurement.toUpperCase()), }) return result } TODO found // TODO check module.reachable let primaryType = '' let primaryValue = '' let secondaryType = '' let secondaryValue = '' // add module sensor measurements switch (module.type) { case this.moduleType.MAIN: result.measurementList.push(this.getMeasurement(module, this.measurement.PRESSURE))Similar blocks of code found in 2 locations. Consider refactoring. if (this.config.showTrend) { result.measurementList.push(this.getMeasurement(module, this.measurement.PRESSURE_TREND)) } result.measurementList.push(this.getMeasurement(module, this.measurement.NOISE)) // break; fallthrough case this.moduleType.INDOOR: if (this.config.design === 'bubbles') { secondaryType = this.measurement.CO2 secondaryValue = module.dashboard_data[secondaryType] result.secondary = { value: this.getValue(secondaryType, secondaryValue), unit: this.getUnit(secondaryType), class: this.kebabCase(secondaryType), visualClass: this.getCO2Status(secondaryValue), } } else { result.measurementList.push(this.getMeasurement(module, this.measurement.CO2)) } // break; fallthrough case this.moduleType.OUTDOOR:Similar blocks of code found in 2 locations. Consider refactoring. if (this.config.design === 'bubbles') { primaryType = this.measurement.TEMPERATURE primaryValue = module.dashboard_data ? module.dashboard_data[primaryType] : '' result.primary = { value: this.getValue(primaryType, primaryValue), unit: this.getUnit(primaryType), class: this.kebabCase(primaryType), } } else { result.measurementList.push(this.getMeasurement(module, this.measurement.TEMPERATURE)) }Similar blocks of code found in 2 locations. Consider refactoring. if (this.config.showTrend) { result.measurementList.push(this.getMeasurement(module, this.measurement.TEMPERATURE_TREND)) } result.measurementList.push(this.getMeasurement(module, this.measurement.HUMIDITY)) break case this.moduleType.WIND: if (this.config.design === 'bubbles') { primaryType = this.measurement.WIND_STRENGTH primaryValue = module.dashboard_data ? module.dashboard_data[primaryType] : '' result.primary = { value: this.getValue(primaryType, primaryValue), unit: this.getUnit(primaryType), class: this.kebabCase(primaryType), } secondaryType = this.measurement.WIND_ANGLE secondaryValue = module.dashboard_data[secondaryType] result.secondary = { value: this.getValue(secondaryType, secondaryValue), unit: this.getUnit(secondaryType), class: this.kebabCase(secondaryType), visualClass: 'xlarge wi wi-direction-up', } } else { result.measurementList.push(this.getMeasurement(module, this.measurement.WIND_STRENGTH)) result.measurementList.push(this.getMeasurement(module, this.measurement.WIND_ANGLE)) } // $('<div/>').addClass('visual xlarge wi wi-direction-up').css('transform', 'rotate(' + value + 'deg)') result.measurementList.push(this.getMeasurement(module, this.measurement.GUST_STRENGTH)) result.measurementList.push(this.getMeasurement(module, this.measurement.GUST_ANGLE)) break case this.moduleType.RAIN:Similar blocks of code found in 2 locations. Consider refactoring. if (this.config.design === 'bubbles') { primaryType = this.measurement.RAIN primaryValue = module.dashboard_data ? module.dashboard_data[primaryType] : '' result.primary = { value: this.getValue(primaryType, primaryValue), unit: this.getUnit(primaryType), class: this.kebabCase(primaryType), } } else { result.measurementList.push(this.getMeasurement(module, this.measurement.RAIN)) } result.measurementList.push(this.getMeasurement(module, this.measurement.RAIN_PER_HOUR)) result.measurementList.push(this.getMeasurement(module, this.measurement.RAIN_PER_DAY)) break default: break } // add module specific measurements if (module.type === this.moduleType.MAIN) { if (this.config.showWiFi) { result.measurementList.push(this.getMeasurement(module, 'wifi', module.wifi_status)) } } else { if (this.config.showRadio) { result.measurementList.push(this.getMeasurement(module, 'radio', module.rf_status)) } if (this.config.showBattery) { result.measurementList.push(this.getMeasurement(module, 'battery', module.battery_percent)) } } // reorder measurements if (this.config.dataOrder && this.config.dataOrder.length > 0) { const reorderedMeasurementList = [] for (const measurementName of this.config.dataOrder) { for (const measurement of result.measurementList) { if (measurement.name === measurementName) { reorderedMeasurementList.push(measurement) } } } result.measurementList = reorderedMeasurementList } return result }, getMeasurement (module, measurement, value) { value = value || module.dashboard_data[measurement] if (measurement === this.measurement.TEMPERATURE_TREND || measurement === this.measurement.PRESSURE_TREND) { value = value || 'undefined' } return { name: measurement, value: this.getValue(measurement, value), unit: this.getUnit(measurement), icon: this.getIcon(measurement, value), label: this.translate(measurement.toUpperCase()), } }, kebabCase (name) { return name.replace(/([a-z])([A-Z])/g, '$1-$2') .replace(/[\s_]+/g, '-') .toLowerCase() },Function `getValue` has 31 lines of code (exceeds 25 allowed). Consider refactoring. getValue (measurement, value) { if (!value) { return value } switch (measurement) { case this.measurement.CO2: return value.toFixed(0)// + ' ppm' case this.measurement.NOISE: return value.toFixed(0)// + ' dB' case this.measurement.HUMIDITY: case 'battery': case 'wifi': case 'radio': return value.toFixed(0)// + '%' case this.measurement.PRESSURE: return this.convertPressureValue(value, this.config.unitOfMeasurementPressure) case this.measurement.TEMPERATURE: return this.convertTemperatureValue(value, this.config.unitOfMeasurement) case this.measurement.RAIN: case this.measurement.RAIN_PER_HOUR: case this.measurement.RAIN_PER_DAY: return this.convertRainValue(value, this.config.unitOfMeasurement) case this.measurement.WIND_STRENGTH: case this.measurement.GUST_STRENGTH: return this.convertWindValue(value, this.config.unitOfMeasurementWind) case this.measurement.WIND_ANGLE: case this.measurement.GUST_ANGLE: return `${this.getDirection(value)} | ${value}`// + '°' case this.measurement.TEMPERATURE_TREND: case this.measurement.PRESSURE_TREND: return this.translate(value.toUpperCase()) default: return value } }, convertTemperatureValue (value, unit) { switch (unit) { case 'IMPERIAL': return (value * 1.8 + 32).toFixed(1) case 'METRIC': default: return value.toFixed(1) } }, convertRainValue (value, unit) { switch (unit) { case 'IMPERIAL': return (value / 25.4).toFixed(1) case 'METRIC': default: return value.toFixed(1) } }, convertPressureValue (value, unit) { switch (unit) { case 'MMHG': return (value / 1.333).toFixed(1) case 'INHG': return (value / 33.864).toFixed(1) case 'MBAR': default: return value.toFixed(0) } }, convertWindValue (value, unit) { switch (unit) { case 'MPH': return (value / 1.609).toFixed(1) case 'MS': return (value / 3.6).toFixed(1) case 'BFT': return this.convertToBeaufort(value) case 'KT': return (value / 1.852).toFixed(1) case 'KPH': default: return value.toFixed(1) } },Function `convertToBeaufort` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring. convertToBeaufort (value) { if (value < 1) return 0 if (value <= 5) return 1 if (value <= 11) return 2 if (value <= 19) return 3Avoid too many `return` statements within this function. if (value <= 28) return 4Avoid too many `return` statements within this function. if (value <= 38) return 5Avoid too many `return` statements within this function. if (value <= 49) return 6Avoid too many `return` statements within this function. if (value <= 61) return 7Avoid too many `return` statements within this function. if (value <= 74) return 8Avoid too many `return` statements within this function. if (value <= 88) return 9Avoid too many `return` statements within this function. if (value <= 102) return 10Avoid too many `return` statements within this function. if (value <= 117) return 11Avoid too many `return` statements within this function. return 12 }, convertNetatmoUnit (unit) { switch (unit) { case 1: return 'IMPERIAL' case 0: default: return 'METRIC' } }, convertNetatmoPressureUnit (unit) { switch (unit) { case 2: return 'MMHG' case 1: return 'INHG' case 0: default: return 'MBAR' } }, convertNetatmoWindUnit (unit) { switch (unit) { case 4: return 'KT' case 3: return 'BFT' case 2: return 'MS' case 1: return 'MPH' case 0: default: return 'KPH' } },Function `getUnit` has 27 lines of code (exceeds 25 allowed). Consider refactoring. getUnit (measurement) { switch (measurement) { case this.measurement.CO2: return 'ppm' case this.measurement.NOISE: return 'dB' case this.measurement.HUMIDITY: case 'battery': case 'wifi': case 'radio': return '%' case this.measurement.PRESSURE: return this.getPressureUnitLabel(this.config.unitOfMeasurementPressure) case this.measurement.TEMPERATURE: return this.getTemperatureUnitLabel(this.config.unitOfMeasurement) case this.measurement.RAIN: case this.measurement.RAIN_PER_HOUR: case this.measurement.RAIN_PER_DAY: return this.getRainUnitLabel(this.config.unitOfMeasurement) case this.measurement.WIND_STRENGTH: case this.measurement.GUST_STRENGTH: return this.getWindUnitLabel(this.config.unitOfMeasurementWind) case this.measurement.WIND_ANGLE: case this.measurement.GUST_ANGLE: return '°' default: return '' } }, getTemperatureUnitLabel (unit) { switch (unit) { case 'IMPERIAL': return '°F' case 'METRIC': default: return '°C' } }, getRainUnitLabel (unit) { switch (unit) { case 'IMPERIAL': return 'in/h' case 'METRIC': default: return 'mm/h' } }, getPressureUnitLabel (unit) { switch (unit) { case 'MMHG': return 'mmHg' case 'INHG': return 'inHg' case 'MBAR': default: return 'mbar' } }, getWindUnitLabel (unit) { switch (unit) { case 'MPH': return 'mph' case 'MS': return 'm/s' case 'BFT': return 'Bft' case 'KT': return 'kt' case 'KPH': default: return 'km/h' } },Function `getDirection` has a Cognitive Complexity of 16 (exceeds 5 allowed). Consider refactoring. getDirection (value) { if (value < 11.25) return 'N' if (value < 33.75) return 'NNE' if (value < 56.25) return 'NE' if (value < 78.75) return 'ENE'Avoid too many `return` statements within this function. if (value < 101.25) return 'E'Avoid too many `return` statements within this function. if (value < 123.75) return 'ESE'Avoid too many `return` statements within this function. if (value < 146.25) return 'SE'Avoid too many `return` statements within this function. if (value < 168.75) return 'SSE'Avoid too many `return` statements within this function. if (value < 191.25) return 'S'Avoid too many `return` statements within this function. if (value < 213.75) return 'SSW'Avoid too many `return` statements within this function. if (value < 236.25) return 'SW'Avoid too many `return` statements within this function. if (value < 258.75) return 'WSW'Avoid too many `return` statements within this function. if (value < 281.25) return 'W'Avoid too many `return` statements within this function. if (value < 303.75) return 'WNW'Avoid too many `return` statements within this function. if (value < 326.25) return 'NW'Avoid too many `return` statements within this function. if (value < 348.75) return 'NNW'Avoid too many `return` statements within this function. return 'N' }, getCO2Status (value) { if (!value || value === 'undefined' || value < 0) return 'undefined' if (value >= this.config.thresholdCO2Bad) return 'bad' if (value >= this.config.thresholdCO2Average) return 'average' return 'good' }, getIcon (dataType, value) { switch (dataType) { // case this.measurement.CO2: // return 'fa-lungs' case this.measurement.NOISE: return 'fa-volume-up' case this.measurement.HUMIDITY: return 'fa-tint' case this.measurement.PRESSURE: return 'fa-tachometer-alt' case this.measurement.GUST_STRENGTH: case this.measurement.WIND_STRENGTH: return 'fa-wind' // case this.measurement.GUST_ANGLE: // case this.measurement.WIND_ANGLE: case this.measurement.PRESSURE_TREND: case this.measurement.TEMPERATURE_TREND: return this.getTrendIcon(value) case 'wifi': return 'fa-wifi' case 'radio': return 'fa-broadcast-tower' case 'battery': return this.getBatteryIcon(value) default: return '' } }, getTrendIcon (value) { if (value === 'stable') return 'fa-chevron-circle-right' if (value === 'down') return 'fa-chevron-circle-down' if (value === 'up') return 'fa-chevron-circle-up' if (value === 'undefined') return 'fa-times-circle' }, getBatteryIcon (value) { if (value > 80) return 'fa-battery-full' if (value > 60) return 'fa-battery-three-quarters' if (value > 40) return 'fa-battery-half' if (value > 20) return 'fa-battery-quarter'Avoid too many `return` statements within this function. return 'fa-battery-empty flash red' }, getStyles () { return [`${this.name}.${this.config.design}.css`] }, getTemplate () { return `${this.name}.${this.config.design}.njk` }, getTemplateData () { return { loaded: this.loaded, showLastMessage: this.config.showLastMessage, showBattery: this.config.showBattery, showRadio: this.config.showRadio, showWiFi: this.config.showWiFi, showTrend: this.config.showTrend, showMeasurementIcon: this.config.showMeasurementIcon, showMeasurementLabel: this.config.showMeasurementLabel, showModuleNameOnTop: this.config.showModuleNameOnTop, horizontal: this.config.horizontal, moduleList: this.moduleList, fontClassModuleName: this.config.fontClassModuleName, fontClassPrimary: this.config.fontClassPrimary, fontClassSecondary: this.config.fontClassSecondary, fontClassMeasurement: this.config.fontClassMeasurement, labelLoading: this.translate('LOADING'), } }, getTranslations () { return { en: 'l10n/en.json', // fallback language cs: 'l10n/cs.json', de: 'l10n/de.json', fr: 'l10n/fr.json', hu: 'l10n/hu.json', nb: 'l10n/nb.json', nn: 'l10n/nn.json', ru: 'l10n/ru.json', sv: 'l10n/sv.json', } },Function `socketNotificationReceived` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring. socketNotificationReceived (notification, payload) { Log.debug(`Netatmo: received ${notification}`) switch (notification) { case this.notifications.AUTH_RESPONSE: if (payload.status === 'OK') { console.log('Netatmo: AUTH OK') this.sendSocketNotification(this.notifications.DATA) } else { console.error(`Netatmo: AUTH FAILED ${payload.message}`) } break case this.notifications.DATA_RESPONSE: if (payload.status === 'OK') { console.log('Devices %o', payload.payloadReturn.devices) const stationList = payload.payloadReturn.devices const userPreferences = payload.payloadReturn.user.administrative this.updateUnitOfMeasurements(userPreferences) this.updateModuleList(stationList) this.updateDom(this.config.animationSpeed) } else if (payload.status === 'INVALID_TOKEN') { // node_module has no valid token, reauthenticate console.error('DATA FAILED, refreshing token') // i'm not agree with this... can have error 403 loop // --> managed with node_helper // this.sendSocketNotification(this.notifications.AUTH) } else { console.error(`Netatmo: DATA FAILED ${payload.message}`) } break } },})