├── APPFont ├── .babelrc ├── .editorconfig ├── .gitignore ├── .postcssrc.js ├── README.md ├── build │ ├── build.js │ ├── check-versions.js │ ├── logo.png │ ├── utils.js │ ├── vue-loader.conf.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ └── webpack.prod.conf.js ├── config │ ├── dev.env.js │ ├── index.js │ └── prod.env.js ├── index.html ├── package-lock.json ├── package.json ├── src │ ├── App.vue │ ├── assets │ │ ├── icon │ │ │ ├── demo.css │ │ │ ├── demo_fontclass.html │ │ │ ├── demo_symbol.html │ │ │ ├── demo_unicode.html │ │ │ ├── iconfont.css │ │ │ ├── iconfont.eot │ │ │ ├── iconfont.js │ │ │ ├── iconfont.svg │ │ │ ├── iconfont.ttf │ │ │ └── iconfont.woff │ │ └── imgs │ │ │ ├── 1.png │ │ │ ├── 3.png │ │ │ ├── 404.jpg │ │ │ ├── EMQ.png │ │ │ ├── dataPoint.png │ │ │ ├── dataStream.png │ │ │ ├── default-head.png │ │ │ ├── device.png │ │ │ ├── head.png │ │ │ ├── logo.png │ │ │ ├── nodeMCU.jpg │ │ │ └── trigger.png │ ├── axios.js │ ├── components │ │ ├── developer │ │ │ ├── charts.vue │ │ │ ├── console.vue │ │ │ ├── dashboard.vue │ │ │ ├── device.vue │ │ │ ├── index.vue │ │ │ ├── leftNavBar.vue │ │ │ ├── stream.vue │ │ │ ├── topNavBar.vue │ │ │ └── trigger.vue │ │ ├── home │ │ │ ├── document.vue │ │ │ ├── footer.vue │ │ │ ├── home.vue │ │ │ ├── introduce.vue │ │ │ ├── resource.vue │ │ │ └── topNavBar.vue │ │ ├── identify.vue │ │ ├── login.vue │ │ ├── notFound.vue │ │ ├── register.vue │ │ └── test.vue │ ├── main.js │ ├── router │ │ └── index.js │ └── store │ │ ├── actions.js │ │ ├── index.js │ │ ├── mutations.js │ │ └── types.js └── static │ └── .gitkeep ├── Hardware └── NodeMCU_MQTT │ └── NodeMCU_MQTT.ino ├── IOTPlatform ├── .idea │ ├── IOTPlatform.iml │ ├── dataSources.local.xml │ ├── dataSources.xml │ ├── dataSources │ │ └── ce1742cf-a77e-4b79-aa2f-b768e4210bdd.xml │ ├── dictionaries │ │ └── fml.xml │ ├── inspectionProfiles │ │ └── Project_Default.xml │ ├── misc.xml │ ├── modules.xml │ └── workspace.xml ├── IOTPlatform │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-36.pyc │ │ ├── __init__.cpython-37.pyc │ │ ├── settings.cpython-36.pyc │ │ ├── settings.cpython-37.pyc │ │ ├── urls.cpython-36.pyc │ │ ├── urls.cpython-37.pyc │ │ ├── wsgi.cpython-36.pyc │ │ └── wsgi.cpython-37.pyc │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── manage.py ├── middleware │ ├── __pycache__ │ │ └── authentication.cpython-36.pyc │ └── authentication.py ├── utils │ ├── __pycache__ │ │ ├── models.cpython-37.pyc │ │ └── time_tools.cpython-37.pyc │ ├── database.py │ ├── models.py │ ├── mqtt_client.py │ └── time_tools.py ├── website │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-36.pyc │ │ ├── __init__.cpython-37.pyc │ │ ├── admin.cpython-36.pyc │ │ ├── admin.cpython-37.pyc │ │ ├── console.cpython-37.pyc │ │ ├── database.cpython-36.pyc │ │ ├── database.cpython-37.pyc │ │ ├── models.cpython-36.pyc │ │ ├── models.cpython-37.pyc │ │ ├── urls.cpython-36.pyc │ │ ├── urls.cpython-37.pyc │ │ ├── views.cpython-36.pyc │ │ └── views.cpython-37.pyc │ ├── admin.py │ ├── apps.py │ ├── console.py │ ├── database.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20181110_0221.py │ │ ├── 0003_auto_20181114_0143.py │ │ ├── 0004_auto_20181114_0144.py │ │ ├── 0005_auto_20181114_0148.py │ │ ├── 0006_auto_20181118_1522.py │ │ ├── 0007_auto_20181118_1618.py │ │ ├── __init__.py │ │ └── __pycache__ │ │ │ ├── 0001_initial.cpython-36.pyc │ │ │ ├── 0001_initial.cpython-37.pyc │ │ │ ├── 0002_auto_20181110_0221.cpython-37.pyc │ │ │ ├── 0003_auto_20181114_0143.cpython-37.pyc │ │ │ ├── 0004_auto_20181114_0144.cpython-37.pyc │ │ │ ├── 0005_auto_20181114_0148.cpython-37.pyc │ │ │ ├── 0006_auto_20181118_1522.cpython-37.pyc │ │ │ ├── 0007_auto_20181118_1618.cpython-37.pyc │ │ │ ├── __init__.cpython-36.pyc │ │ │ └── __init__.cpython-37.pyc │ ├── models.py │ ├── tests.py │ ├── urls.py │ ├── utils │ │ ├── __pycache__ │ │ │ ├── auth.cpython-37.pyc │ │ │ ├── create.cpython-36.pyc │ │ │ └── create.cpython-37.pyc │ │ ├── auth.py │ │ └── create.py │ └── views.py └── weixin │ ├── __init__.py │ ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── __init__.cpython-37.pyc │ ├── admin.cpython-36.pyc │ ├── admin.cpython-37.pyc │ ├── models.cpython-36.pyc │ ├── models.cpython-37.pyc │ ├── urls.cpython-36.pyc │ ├── urls.cpython-37.pyc │ ├── views.cpython-36.pyc │ └── views.cpython-37.pyc │ ├── admin.py │ ├── apps.py │ ├── migrations │ ├── __init__.py │ └── __pycache__ │ │ ├── __init__.cpython-36.pyc │ │ └── __init__.cpython-37.pyc │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── MQTT ├── .idea │ ├── MQTT.iml │ ├── dictionaries │ │ └── fml.xml │ ├── inspectionProfiles │ │ └── Project_Default.xml │ ├── misc.xml │ ├── modules.xml │ └── workspace.xml ├── __pycache__ │ ├── database.cpython-37.pyc │ └── models.cpython-37.pyc ├── database.py ├── models.py └── mqtt_client.py └── readme.md /APPFont/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"] 12 | } 13 | -------------------------------------------------------------------------------- /APPFont/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /APPFont/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /APPFont/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /APPFont/README.md: -------------------------------------------------------------------------------- 1 | # cms 2 | 3 | > A Vue.js project 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | npm install 10 | 11 | # serve with hot reload at localhost:8080 12 | npm run dev 13 | 14 | # build for production with minification 15 | npm run build 16 | 17 | # build for production and view the bundle analyzer report 18 | npm run build --report 19 | ``` 20 | 21 | For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). 22 | -------------------------------------------------------------------------------- /APPFont/build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, (err, stats) => { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /APPFont/build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | 7 | function exec (cmd) { 8 | return require('child_process').execSync(cmd).toString().trim() 9 | } 10 | 11 | const versionRequirements = [ 12 | { 13 | name: 'node', 14 | currentVersion: semver.clean(process.version), 15 | versionRequirement: packageConfig.engines.node 16 | } 17 | ] 18 | 19 | if (shell.which('npm')) { 20 | versionRequirements.push({ 21 | name: 'npm', 22 | currentVersion: exec('npm --version'), 23 | versionRequirement: packageConfig.engines.npm 24 | }) 25 | } 26 | 27 | module.exports = function () { 28 | const warnings = [] 29 | 30 | for (let i = 0; i < versionRequirements.length; i++) { 31 | const mod = versionRequirements[i] 32 | 33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 34 | warnings.push(mod.name + ': ' + 35 | chalk.red(mod.currentVersion) + ' should be ' + 36 | chalk.green(mod.versionRequirement) 37 | ) 38 | } 39 | } 40 | 41 | if (warnings.length) { 42 | console.log('') 43 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 44 | console.log() 45 | 46 | for (let i = 0; i < warnings.length; i++) { 47 | const warning = warnings[i] 48 | console.log(' ' + warning) 49 | } 50 | 51 | console.log() 52 | process.exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /APPFont/build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/build/logo.png -------------------------------------------------------------------------------- /APPFont/build/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const config = require('../config') 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | const packageConfig = require('../package.json') 6 | 7 | exports.assetsPath = function (_path) { 8 | const assetsSubDirectory = process.env.NODE_ENV === 'production' 9 | ? config.build.assetsSubDirectory 10 | : config.dev.assetsSubDirectory 11 | 12 | return path.posix.join(assetsSubDirectory, _path) 13 | } 14 | 15 | exports.cssLoaders = function (options) { 16 | options = options || {} 17 | 18 | const cssLoader = { 19 | loader: 'css-loader', 20 | options: { 21 | sourceMap: options.sourceMap 22 | } 23 | } 24 | 25 | const postcssLoader = { 26 | loader: 'postcss-loader', 27 | options: { 28 | sourceMap: options.sourceMap 29 | } 30 | } 31 | 32 | // generate loader string to be used with extract text plugin 33 | function generateLoaders (loader, loaderOptions) { 34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] 35 | 36 | if (loader) { 37 | loaders.push({ 38 | loader: loader + '-loader', 39 | options: Object.assign({}, loaderOptions, { 40 | sourceMap: options.sourceMap 41 | }) 42 | }) 43 | } 44 | 45 | // Extract CSS when that option is specified 46 | // (which is the case during production build) 47 | if (options.extract) { 48 | return ExtractTextPlugin.extract({ 49 | use: loaders, 50 | fallback: 'vue-style-loader' 51 | }) 52 | } else { 53 | return ['vue-style-loader'].concat(loaders) 54 | } 55 | } 56 | 57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 58 | return { 59 | css: generateLoaders(), 60 | postcss: generateLoaders(), 61 | less: generateLoaders('less'), 62 | sass: generateLoaders('sass', { indentedSyntax: true }), 63 | scss: generateLoaders('sass'), 64 | stylus: generateLoaders('stylus'), 65 | styl: generateLoaders('stylus') 66 | } 67 | } 68 | 69 | // Generate loaders for standalone style files (outside of .vue) 70 | exports.styleLoaders = function (options) { 71 | const output = [] 72 | const loaders = exports.cssLoaders(options) 73 | 74 | for (const extension in loaders) { 75 | const loader = loaders[extension] 76 | output.push({ 77 | test: new RegExp('\\.' + extension + '$'), 78 | use: loader 79 | }) 80 | } 81 | 82 | return output 83 | } 84 | 85 | exports.createNotifierCallback = () => { 86 | const notifier = require('node-notifier') 87 | 88 | return (severity, errors) => { 89 | if (severity !== 'error') return 90 | 91 | const error = errors[0] 92 | const filename = error.file && error.file.split('!').pop() 93 | 94 | notifier.notify({ 95 | title: packageConfig.name, 96 | message: severity + ': ' + error.name, 97 | subtitle: filename || '', 98 | icon: path.join(__dirname, 'logo.png') 99 | }) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /APPFont/build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | module.exports = { 10 | loaders: utils.cssLoaders({ 11 | sourceMap: sourceMapEnabled, 12 | extract: isProduction 13 | }), 14 | cssSourceMap: sourceMapEnabled, 15 | cacheBusting: config.dev.cacheBusting, 16 | transformToRequire: { 17 | video: ['src', 'poster'], 18 | source: 'src', 19 | img: 'src', 20 | image: 'xlink:href' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /APPFont/build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const vueLoaderConfig = require('./vue-loader.conf') 6 | 7 | function resolve (dir) { 8 | return path.join(__dirname, '..', dir) 9 | } 10 | 11 | 12 | 13 | module.exports = { 14 | context: path.resolve(__dirname, '../'), 15 | entry: { 16 | app: './src/main.js' 17 | }, 18 | output: { 19 | path: config.build.assetsRoot, 20 | filename: '[name].js', 21 | publicPath: process.env.NODE_ENV === 'production' 22 | ? config.build.assetsPublicPath 23 | : config.dev.assetsPublicPath 24 | }, 25 | resolve: { 26 | extensions: ['.js', '.vue', '.json'], 27 | alias: { 28 | 'vue$': 'vue/dist/vue.esm.js', 29 | '@': resolve('src'), 30 | } 31 | }, 32 | module: { 33 | rules: [ 34 | { 35 | test: /\.vue$/, 36 | loader: 'vue-loader', 37 | options: vueLoaderConfig 38 | }, 39 | { 40 | test: /\.js$/, 41 | loader: 'babel-loader', 42 | include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] 43 | }, 44 | { 45 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 46 | loader: 'url-loader', 47 | options: { 48 | limit: 10000, 49 | name: utils.assetsPath('imgs/[name].[hash:7].[ext]') 50 | } 51 | }, 52 | { 53 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 54 | loader: 'url-loader', 55 | options: { 56 | limit: 10000, 57 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 58 | } 59 | }, 60 | { 61 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 62 | loader: 'url-loader', 63 | options: { 64 | limit: 10000, 65 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 66 | } 67 | } 68 | ] 69 | }, 70 | node: { 71 | // prevent webpack from injecting useless setImmediate polyfill because Vue 72 | // source contains it (although only uses it if it's native). 73 | setImmediate: false, 74 | // prevent webpack from injecting mocks to Node native modules 75 | // that does not make sense for the client 76 | dgram: 'empty', 77 | fs: 'empty', 78 | net: 'empty', 79 | tls: 'empty', 80 | child_process: 'empty' 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /APPFont/build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const webpack = require('webpack') 4 | const config = require('../config') 5 | const merge = require('webpack-merge') 6 | const path = require('path') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 11 | const portfinder = require('portfinder') 12 | 13 | const HOST = process.env.HOST 14 | const PORT = process.env.PORT && Number(process.env.PORT) 15 | 16 | const devWebpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) 19 | }, 20 | // cheap-module-eval-source-map is faster for development 21 | devtool: config.dev.devtool, 22 | 23 | // these devServer options should be customized in /config/index.js 24 | devServer: { 25 | clientLogLevel: 'warning', 26 | historyApiFallback: { 27 | rewrites: [ 28 | { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, 29 | ], 30 | }, 31 | hot: true, 32 | contentBase: false, // since we use CopyWebpackPlugin. 33 | compress: true, 34 | host: HOST || config.dev.host, 35 | port: PORT || config.dev.port, 36 | open: config.dev.autoOpenBrowser, 37 | overlay: config.dev.errorOverlay 38 | ? { warnings: false, errors: true } 39 | : false, 40 | publicPath: config.dev.assetsPublicPath, 41 | proxy: config.dev.proxyTable, 42 | quiet: true, // necessary for FriendlyErrorsPlugin 43 | watchOptions: { 44 | poll: config.dev.poll, 45 | } 46 | }, 47 | plugins: [ 48 | new webpack.DefinePlugin({ 49 | 'process.env': require('../config/dev.env') 50 | }), 51 | new webpack.HotModuleReplacementPlugin(), 52 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. 53 | new webpack.NoEmitOnErrorsPlugin(), 54 | // https://github.com/ampedandwired/html-webpack-plugin 55 | new HtmlWebpackPlugin({ 56 | filename: 'index.html', 57 | template: 'index.html', 58 | inject: true 59 | }), 60 | // copy custom static assets 61 | new CopyWebpackPlugin([ 62 | { 63 | from: path.resolve(__dirname, '../static'), 64 | to: config.dev.assetsSubDirectory, 65 | ignore: ['.*'] 66 | } 67 | ]) 68 | ] 69 | }) 70 | 71 | module.exports = new Promise((resolve, reject) => { 72 | portfinder.basePort = process.env.PORT || config.dev.port 73 | portfinder.getPort((err, port) => { 74 | if (err) { 75 | reject(err) 76 | } else { 77 | // publish the new Port, necessary for e2e tests 78 | process.env.PORT = port 79 | // add port to devServer config 80 | devWebpackConfig.devServer.port = port 81 | 82 | // Add FriendlyErrorsPlugin 83 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ 84 | compilationSuccessInfo: { 85 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], 86 | }, 87 | onErrors: config.dev.notifyOnErrors 88 | ? utils.createNotifierCallback() 89 | : undefined 90 | })) 91 | 92 | resolve(devWebpackConfig) 93 | } 94 | }) 95 | }) 96 | -------------------------------------------------------------------------------- /APPFont/build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const webpack = require('webpack') 5 | const config = require('../config') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 12 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 13 | 14 | const env = require('../config/prod.env') 15 | 16 | const webpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ 19 | sourceMap: config.build.productionSourceMap, 20 | extract: true, 21 | usePostCSS: true 22 | }) 23 | }, 24 | devtool: config.build.productionSourceMap ? config.build.devtool : false, 25 | output: { 26 | path: config.build.assetsRoot, 27 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 28 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 29 | }, 30 | plugins: [ 31 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 32 | new webpack.DefinePlugin({ 33 | 'process.env': env 34 | }), 35 | new UglifyJsPlugin({ 36 | uglifyOptions: { 37 | compress: { 38 | warnings: false 39 | } 40 | }, 41 | sourceMap: config.build.productionSourceMap, 42 | parallel: true 43 | }), 44 | // extract css into its own file 45 | new ExtractTextPlugin({ 46 | filename: utils.assetsPath('css/[name].[contenthash].css'), 47 | // Setting the following option to `false` will not extract CSS from codesplit chunks. 48 | // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. 49 | // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 50 | // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 51 | allChunks: true, 52 | }), 53 | // Compress extracted CSS. We are using this plugin so that possible 54 | // duplicated CSS from different components can be deduped. 55 | new OptimizeCSSPlugin({ 56 | cssProcessorOptions: config.build.productionSourceMap 57 | ? { safe: true, map: { inline: false } } 58 | : { safe: true } 59 | }), 60 | // generate dist index.html with correct asset hash for caching. 61 | // you can customize output by editing /index.html 62 | // see https://github.com/ampedandwired/html-webpack-plugin 63 | new HtmlWebpackPlugin({ 64 | filename: config.build.index, 65 | template: 'index.html', 66 | inject: true, 67 | minify: { 68 | removeComments: true, 69 | collapseWhitespace: true, 70 | removeAttributeQuotes: true 71 | // more options: 72 | // https://github.com/kangax/html-minifier#options-quick-reference 73 | }, 74 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 75 | chunksSortMode: 'dependency' 76 | }), 77 | // keep module.id stable when vendor modules does not change 78 | new webpack.HashedModuleIdsPlugin(), 79 | // enable scope hoisting 80 | new webpack.optimize.ModuleConcatenationPlugin(), 81 | // split vendor js into its own file 82 | new webpack.optimize.CommonsChunkPlugin({ 83 | name: 'vendor', 84 | minChunks (module) { 85 | // any required modules inside node_modules are extracted to vendor 86 | return ( 87 | module.resource && 88 | /\.js$/.test(module.resource) && 89 | module.resource.indexOf( 90 | path.join(__dirname, '../node_modules') 91 | ) === 0 92 | ) 93 | } 94 | }), 95 | // extract webpack runtime and module manifest to its own file in order to 96 | // prevent vendor hash from being updated whenever app bundle is updated 97 | new webpack.optimize.CommonsChunkPlugin({ 98 | name: 'manifest', 99 | minChunks: Infinity 100 | }), 101 | // This instance extracts shared chunks from code splitted chunks and bundles them 102 | // in a separate chunk, similar to the vendor chunk 103 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk 104 | new webpack.optimize.CommonsChunkPlugin({ 105 | name: 'app', 106 | async: 'vendor-async', 107 | children: true, 108 | minChunks: 3 109 | }), 110 | 111 | // copy custom static assets 112 | new CopyWebpackPlugin([ 113 | { 114 | from: path.resolve(__dirname, '../static'), 115 | to: config.build.assetsSubDirectory, 116 | ignore: ['.*'] 117 | } 118 | ]) 119 | ] 120 | }) 121 | 122 | if (config.build.productionGzip) { 123 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 124 | 125 | webpackConfig.plugins.push( 126 | new CompressionWebpackPlugin({ 127 | asset: '[path].gz[query]', 128 | algorithm: 'gzip', 129 | test: new RegExp( 130 | '\\.(' + 131 | config.build.productionGzipExtensions.join('|') + 132 | ')$' 133 | ), 134 | threshold: 10240, 135 | minRatio: 0.8 136 | }) 137 | ) 138 | } 139 | 140 | if (config.build.bundleAnalyzerReport) { 141 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 142 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 143 | } 144 | 145 | module.exports = webpackConfig 146 | -------------------------------------------------------------------------------- /APPFont/config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /APPFont/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: {}, 14 | 15 | // Various Dev Server settings 16 | host: 'localhost', // can be overwritten by process.env.HOST 17 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 18 | autoOpenBrowser: false, 19 | errorOverlay: true, 20 | notifyOnErrors: true, 21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 22 | 23 | 24 | /** 25 | * Source Maps 26 | */ 27 | 28 | // https://webpack.js.org/configuration/devtool/#development 29 | devtool: 'cheap-module-eval-source-map', 30 | 31 | // If you have problems debugging vue-files in devtools, 32 | // set this to false - it *may* help 33 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 34 | cacheBusting: true, 35 | 36 | cssSourceMap: true 37 | }, 38 | 39 | build: { 40 | // Template for index.html 41 | index: path.resolve(__dirname, '../dist/index.html'), 42 | 43 | // Paths 44 | assetsRoot: path.resolve(__dirname, '../dist'), 45 | assetsSubDirectory: 'static', 46 | assetsPublicPath: '/', 47 | 48 | /** 49 | * Source Maps 50 | */ 51 | 52 | productionSourceMap: true, 53 | // https://webpack.js.org/configuration/devtool/#production 54 | devtool: '#source-map', 55 | 56 | // Gzip off by default as many popular static hosts such as 57 | // Surge or Netlify already gzip all static assets for you. 58 | // Before setting to `true`, make sure to: 59 | // npm install --save-dev compression-webpack-plugin 60 | productionGzip: false, 61 | productionGzipExtensions: ['js', 'css'], 62 | 63 | // Run the build command with an extra argument to 64 | // View the bundle analyzer report after build finishes: 65 | // `npm run build --report` 66 | // Set to `true` or `false` to always turn it on or off 67 | bundleAnalyzerReport: process.env.npm_config_report 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /APPFont/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /APPFont/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | IOTPlatform 8 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /APPFont/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cms", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "FanMLei <15638515832@163.com>", 6 | "private": true, 7 | "scripts": { 8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 9 | "start": "npm run dev", 10 | "build": "node build/build.js" 11 | }, 12 | "dependencies": { 13 | "axios": "^0.18.0", 14 | "echarts": "^4.2.0-rc.2", 15 | "element-ui": "^2.4.9", 16 | "vue": "^2.5.2", 17 | "vue-router": "^3.0.1", 18 | "vuex": "^3.0.1" 19 | }, 20 | "devDependencies": { 21 | "autoprefixer": "^7.1.2", 22 | "babel-core": "^6.22.1", 23 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 24 | "babel-loader": "^7.1.1", 25 | "babel-plugin-syntax-jsx": "^6.18.0", 26 | "babel-plugin-transform-runtime": "^6.22.0", 27 | "babel-plugin-transform-vue-jsx": "^3.5.0", 28 | "babel-preset-env": "^1.3.2", 29 | "babel-preset-stage-2": "^6.22.0", 30 | "chalk": "^2.0.1", 31 | "copy-webpack-plugin": "^4.0.1", 32 | "css-loader": "^0.28.0", 33 | "extract-text-webpack-plugin": "^3.0.0", 34 | "file-loader": "^1.1.4", 35 | "friendly-errors-webpack-plugin": "^1.6.1", 36 | "html-webpack-plugin": "^2.30.1", 37 | "node-notifier": "^5.1.2", 38 | "optimize-css-assets-webpack-plugin": "^3.2.0", 39 | "ora": "^1.2.0", 40 | "portfinder": "^1.0.13", 41 | "postcss-import": "^11.0.0", 42 | "postcss-loader": "^2.0.8", 43 | "postcss-url": "^7.2.1", 44 | "rimraf": "^2.6.0", 45 | "semver": "^5.3.0", 46 | "shelljs": "^0.7.6", 47 | "uglifyjs-webpack-plugin": "^1.1.1", 48 | "url-loader": "^0.5.8", 49 | "vue-loader": "^13.3.0", 50 | "vue-style-loader": "^3.0.1", 51 | "vue-template-compiler": "^2.5.2", 52 | "webpack": "^3.6.0", 53 | "webpack-bundle-analyzer": "^2.9.0", 54 | "webpack-dev-server": "^2.9.1", 55 | "webpack-merge": "^4.1.0" 56 | }, 57 | "engines": { 58 | "node": ">= 6.0.0", 59 | "npm": ">= 3.0.0" 60 | }, 61 | "browserslist": [ 62 | "> 1%", 63 | "last 2 versions", 64 | "not ie <= 8" 65 | ] 66 | } 67 | -------------------------------------------------------------------------------- /APPFont/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /APPFont/src/assets/icon/demo.css: -------------------------------------------------------------------------------- 1 | *{margin: 0;padding: 0;list-style: none;} 2 | /* 3 | KISSY CSS Reset 4 | 理念:1. reset 的目的不是清除浏览器的默认样式,这仅是部分工作。清除和重置是紧密不可分的。 5 | 2. reset 的目的不是让默认样式在所有浏览器下一致,而是减少默认样式有可能带来的问题。 6 | 3. reset 期望提供一套普适通用的基础样式。但没有银弹,推荐根据具体需求,裁剪和修改后再使用。 7 | 特色:1. 适应中文;2. 基于最新主流浏览器。 8 | 维护:玉伯, 正淳 9 | */ 10 | 11 | /** 清除内外边距 **/ 12 | body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, /* structural elements 结构元素 */ 13 | dl, dt, dd, ul, ol, li, /* list elements 列表元素 */ 14 | pre, /* text formatting elements 文本格式元素 */ 15 | form, fieldset, legend, button, input, textarea, /* form elements 表单元素 */ 16 | th, td /* table elements 表格元素 */ { 17 | margin: 0; 18 | padding: 0; 19 | } 20 | 21 | /** 设置默认字体 **/ 22 | body, 23 | button, input, select, textarea /* for ie */ { 24 | font: 12px/1.5 tahoma, arial, \5b8b\4f53, sans-serif; 25 | } 26 | h1, h2, h3, h4, h5, h6 { font-size: 100%; } 27 | address, cite, dfn, em, var { font-style: normal; } /* 将斜体扶正 */ 28 | code, kbd, pre, samp { font-family: courier new, courier, monospace; } /* 统一等宽字体 */ 29 | small { font-size: 12px; } /* 小于 12px 的中文很难阅读,让 small 正常化 */ 30 | 31 | /** 重置列表元素 **/ 32 | ul, ol { list-style: none; } 33 | 34 | /** 重置文本格式元素 **/ 35 | a { text-decoration: none; } 36 | a:hover { text-decoration: underline; } 37 | 38 | 39 | /** 重置表单元素 **/ 40 | legend { color: #000; } /* for ie6 */ 41 | fieldset, img { border: 0; } /* img 搭车:让链接里的 img 无边框 */ 42 | button, input, select, textarea { font-size: 100%; } /* 使得表单元素在 ie 下能继承字体大小 */ 43 | /* 注:optgroup 无法扶正 */ 44 | 45 | /** 重置表格元素 **/ 46 | table { border-collapse: collapse; border-spacing: 0; } 47 | 48 | /* 清除浮动 */ 49 | .ks-clear:after, .clear:after { 50 | content: '\20'; 51 | display: block; 52 | height: 0; 53 | clear: both; 54 | } 55 | .ks-clear, .clear { 56 | *zoom: 1; 57 | } 58 | 59 | .main { 60 | padding: 30px 100px; 61 | width: 960px; 62 | margin: 0 auto; 63 | } 64 | .main h1{font-size:36px; color:#333; text-align:left;margin-bottom:30px; border-bottom: 1px solid #eee;} 65 | 66 | .helps{margin-top:40px;} 67 | .helps pre{ 68 | padding:20px; 69 | margin:10px 0; 70 | border:solid 1px #e7e1cd; 71 | background-color: #fffdef; 72 | overflow: auto; 73 | } 74 | 75 | .icon_lists{ 76 | width: 100% !important; 77 | 78 | } 79 | 80 | .icon_lists li{ 81 | float:left; 82 | width: 100px; 83 | height:180px; 84 | text-align: center; 85 | list-style: none !important; 86 | } 87 | .icon_lists .icon{ 88 | font-size: 42px; 89 | line-height: 100px; 90 | margin: 10px 0; 91 | color:#333; 92 | -webkit-transition: font-size 0.25s ease-out 0s; 93 | -moz-transition: font-size 0.25s ease-out 0s; 94 | transition: font-size 0.25s ease-out 0s; 95 | 96 | } 97 | .icon_lists .icon:hover{ 98 | font-size: 100px; 99 | } 100 | 101 | 102 | 103 | .markdown { 104 | color: #666; 105 | font-size: 14px; 106 | line-height: 1.8; 107 | } 108 | 109 | .highlight { 110 | line-height: 1.5; 111 | } 112 | 113 | .markdown img { 114 | vertical-align: middle; 115 | max-width: 100%; 116 | } 117 | 118 | .markdown h1 { 119 | color: #404040; 120 | font-weight: 500; 121 | line-height: 40px; 122 | margin-bottom: 24px; 123 | } 124 | 125 | .markdown h2, 126 | .markdown h3, 127 | .markdown h4, 128 | .markdown h5, 129 | .markdown h6 { 130 | color: #404040; 131 | margin: 1.6em 0 0.6em 0; 132 | font-weight: 500; 133 | clear: both; 134 | } 135 | 136 | .markdown h1 { 137 | font-size: 28px; 138 | } 139 | 140 | .markdown h2 { 141 | font-size: 22px; 142 | } 143 | 144 | .markdown h3 { 145 | font-size: 16px; 146 | } 147 | 148 | .markdown h4 { 149 | font-size: 14px; 150 | } 151 | 152 | .markdown h5 { 153 | font-size: 12px; 154 | } 155 | 156 | .markdown h6 { 157 | font-size: 12px; 158 | } 159 | 160 | .markdown hr { 161 | height: 1px; 162 | border: 0; 163 | background: #e9e9e9; 164 | margin: 16px 0; 165 | clear: both; 166 | } 167 | 168 | .markdown p, 169 | .markdown pre { 170 | margin: 1em 0; 171 | } 172 | 173 | .markdown > p, 174 | .markdown > blockquote, 175 | .markdown > .highlight, 176 | .markdown > ol, 177 | .markdown > ul { 178 | width: 80%; 179 | } 180 | 181 | .markdown ul > li { 182 | list-style: circle; 183 | } 184 | 185 | .markdown > ul li, 186 | .markdown blockquote ul > li { 187 | margin-left: 20px; 188 | padding-left: 4px; 189 | } 190 | 191 | .markdown > ul li p, 192 | .markdown > ol li p { 193 | margin: 0.6em 0; 194 | } 195 | 196 | .markdown ol > li { 197 | list-style: decimal; 198 | } 199 | 200 | .markdown > ol li, 201 | .markdown blockquote ol > li { 202 | margin-left: 20px; 203 | padding-left: 4px; 204 | } 205 | 206 | .markdown code { 207 | margin: 0 3px; 208 | padding: 0 5px; 209 | background: #eee; 210 | border-radius: 3px; 211 | } 212 | 213 | .markdown pre { 214 | border-radius: 6px; 215 | background: #f7f7f7; 216 | padding: 20px; 217 | } 218 | 219 | .markdown pre code { 220 | border: none; 221 | background: #f7f7f7; 222 | margin: 0; 223 | } 224 | 225 | .markdown strong, 226 | .markdown b { 227 | font-weight: 600; 228 | } 229 | 230 | .markdown > table { 231 | border-collapse: collapse; 232 | border-spacing: 0px; 233 | empty-cells: show; 234 | border: 1px solid #e9e9e9; 235 | width: 95%; 236 | margin-bottom: 24px; 237 | } 238 | 239 | .markdown > table th { 240 | white-space: nowrap; 241 | color: #333; 242 | font-weight: 600; 243 | 244 | } 245 | 246 | .markdown > table th, 247 | .markdown > table td { 248 | border: 1px solid #e9e9e9; 249 | padding: 8px 16px; 250 | text-align: left; 251 | } 252 | 253 | .markdown > table th { 254 | background: #F7F7F7; 255 | } 256 | 257 | .markdown blockquote { 258 | font-size: 90%; 259 | color: #999; 260 | border-left: 4px solid #e9e9e9; 261 | padding-left: 0.8em; 262 | margin: 1em 0; 263 | font-style: italic; 264 | } 265 | 266 | .markdown blockquote p { 267 | margin: 0; 268 | } 269 | 270 | .markdown .anchor { 271 | opacity: 0; 272 | transition: opacity 0.3s ease; 273 | margin-left: 8px; 274 | } 275 | 276 | .markdown .waiting { 277 | color: #ccc; 278 | } 279 | 280 | .markdown h1:hover .anchor, 281 | .markdown h2:hover .anchor, 282 | .markdown h3:hover .anchor, 283 | .markdown h4:hover .anchor, 284 | .markdown h5:hover .anchor, 285 | .markdown h6:hover .anchor { 286 | opacity: 1; 287 | display: inline-block; 288 | } 289 | 290 | .markdown > br, 291 | .markdown > p > br { 292 | clear: both; 293 | } 294 | 295 | 296 | .hljs { 297 | display: block; 298 | background: white; 299 | padding: 0.5em; 300 | color: #333333; 301 | overflow-x: auto; 302 | } 303 | 304 | .hljs-comment, 305 | .hljs-meta { 306 | color: #969896; 307 | } 308 | 309 | .hljs-string, 310 | .hljs-variable, 311 | .hljs-template-variable, 312 | .hljs-strong, 313 | .hljs-emphasis, 314 | .hljs-quote { 315 | color: #df5000; 316 | } 317 | 318 | .hljs-keyword, 319 | .hljs-selector-tag, 320 | .hljs-type { 321 | color: #a71d5d; 322 | } 323 | 324 | .hljs-literal, 325 | .hljs-symbol, 326 | .hljs-bullet, 327 | .hljs-attribute { 328 | color: #0086b3; 329 | } 330 | 331 | .hljs-section, 332 | .hljs-name { 333 | color: #63a35c; 334 | } 335 | 336 | .hljs-tag { 337 | color: #333333; 338 | } 339 | 340 | .hljs-title, 341 | .hljs-attr, 342 | .hljs-selector-id, 343 | .hljs-selector-class, 344 | .hljs-selector-attr, 345 | .hljs-selector-pseudo { 346 | color: #795da3; 347 | } 348 | 349 | .hljs-addition { 350 | color: #55a532; 351 | background-color: #eaffea; 352 | } 353 | 354 | .hljs-deletion { 355 | color: #bd2c00; 356 | background-color: #ffecec; 357 | } 358 | 359 | .hljs-link { 360 | text-decoration: underline; 361 | } 362 | 363 | pre{ 364 | background: #fff; 365 | } 366 | 367 | 368 | 369 | 370 | 371 | -------------------------------------------------------------------------------- /APPFont/src/assets/icon/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/src/assets/icon/iconfont.eot -------------------------------------------------------------------------------- /APPFont/src/assets/icon/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/src/assets/icon/iconfont.ttf -------------------------------------------------------------------------------- /APPFont/src/assets/icon/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/src/assets/icon/iconfont.woff -------------------------------------------------------------------------------- /APPFont/src/assets/imgs/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/src/assets/imgs/1.png -------------------------------------------------------------------------------- /APPFont/src/assets/imgs/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/src/assets/imgs/3.png -------------------------------------------------------------------------------- /APPFont/src/assets/imgs/404.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/src/assets/imgs/404.jpg -------------------------------------------------------------------------------- /APPFont/src/assets/imgs/EMQ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/src/assets/imgs/EMQ.png -------------------------------------------------------------------------------- /APPFont/src/assets/imgs/dataPoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/src/assets/imgs/dataPoint.png -------------------------------------------------------------------------------- /APPFont/src/assets/imgs/dataStream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/src/assets/imgs/dataStream.png -------------------------------------------------------------------------------- /APPFont/src/assets/imgs/default-head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/src/assets/imgs/default-head.png -------------------------------------------------------------------------------- /APPFont/src/assets/imgs/device.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/src/assets/imgs/device.png -------------------------------------------------------------------------------- /APPFont/src/assets/imgs/head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/src/assets/imgs/head.png -------------------------------------------------------------------------------- /APPFont/src/assets/imgs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/src/assets/imgs/logo.png -------------------------------------------------------------------------------- /APPFont/src/assets/imgs/nodeMCU.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/src/assets/imgs/nodeMCU.jpg -------------------------------------------------------------------------------- /APPFont/src/assets/imgs/trigger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/src/assets/imgs/trigger.png -------------------------------------------------------------------------------- /APPFont/src/axios.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import store from './store' 3 | import router from './router' 4 | 5 | //设置全局axios默认值 6 | // axios.defaults.timeout = 5000; //5000的超时验证 7 | // axios.defaults.baseURL = 'http://127.0.0.1:8000/'; 8 | axios.defaults.baseURL = 'http://123.207.87.193:8000/'; 9 | axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'; 10 | 11 | //创建一个axios实例 12 | const instance = axios.create(); 13 | instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'; 14 | // axios.defaults.baseURL = 'http://127.0.0.1:8000/'; 15 | axios.interceptors.request.use = instance.interceptors.request.use; 16 | 17 | //request拦截器 18 | instance.interceptors.request.use( 19 | config => { 20 | //每次发送请求之前检测都vuex存有token,那么都要放在请求头发送给服务器 21 | if (store.state.token) { 22 | config.headers.Authorization = `${store.state.token}`; 23 | } 24 | return config; 25 | }, 26 | err => { 27 | return Promise.reject(err); 28 | } 29 | ); 30 | //respone拦截器 31 | instance.interceptors.response.use( 32 | response => { 33 | return response; 34 | }, 35 | error => { //默认除了2XX之外的都是错误的,就会走这里 36 | if (error.response) { 37 | switch (error.response.status) { 38 | case 401: 39 | store.dispatch('UserLogout'); //可能是token过期,清除它 40 | router.replace({ //跳转到登录页面 41 | path: 'login', 42 | query: {redirect: router.currentRoute.fullPath} // 将跳转的路由path作为参数,登录成功后跳转到该路由 43 | }); 44 | } 45 | } 46 | return Promise.reject(error.response); 47 | } 48 | ); 49 | 50 | export default { 51 | //用户注册 52 | userRegister(data) { 53 | return instance.post('/register', data); 54 | }, 55 | //用户登录 56 | userLogin(data) { 57 | return instance.post('/login', data); 58 | }, 59 | //获取用户 60 | getUser() { 61 | return instance.get('/api/user'); 62 | }, 63 | //获取概览页面数据 64 | dashboard() { 65 | return instance.get('/dashboard'); 66 | }, 67 | //获取历史数据 68 | charts() { 69 | return instance.get('/charts'); 70 | }, 71 | // 获取设备信息 72 | getDevice() { 73 | return instance.get('/devices'); 74 | }, 75 | // 删除设备 76 | delDevice(id,apiKey) { 77 | return instance.delete('/devices', {data:{id:id,apiKey:apiKey}}); 78 | }, 79 | // 更新设备信息 80 | updateDevice(data) { 81 | return instance.put('/devices', data) 82 | }, 83 | // 新增设备 84 | addDevice(data) { 85 | return instance.post('/devices', data); 86 | }, 87 | updateStream(data) { 88 | return instance.put('/streams', data) 89 | }, 90 | addStream(data) { 91 | return instance.post('/streams', data); 92 | }, 93 | delStream(id,name) { 94 | return instance.delete('/streams', {data:{dev_id:id,name:name}}); 95 | }, 96 | getStream(){ 97 | return instance.get('/streams'); 98 | }, 99 | publish(data){ 100 | return instance.post('/console', data) 101 | }, 102 | getTrigger(){ 103 | return instance.get('/triggers') 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /APPFont/src/components/developer/charts.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 102 | 103 | 115 | -------------------------------------------------------------------------------- /APPFont/src/components/developer/console.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 133 | 134 | 169 | -------------------------------------------------------------------------------- /APPFont/src/components/developer/dashboard.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 131 | 132 | 202 | -------------------------------------------------------------------------------- /APPFont/src/components/developer/device.vue: -------------------------------------------------------------------------------- 1 | 142 | 143 | 283 | 284 | 331 | -------------------------------------------------------------------------------- /APPFont/src/components/developer/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 32 | 64 | -------------------------------------------------------------------------------- /APPFont/src/components/developer/leftNavBar.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 51 | 52 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /APPFont/src/components/developer/topNavBar.vue: -------------------------------------------------------------------------------- 1 | 27 | 42 | 68 | -------------------------------------------------------------------------------- /APPFont/src/components/developer/trigger.vue: -------------------------------------------------------------------------------- 1 | 63 | 64 | 82 | 83 | 98 | -------------------------------------------------------------------------------- /APPFont/src/components/home/document.vue: -------------------------------------------------------------------------------- 1 | 9 | 20 | 29 | -------------------------------------------------------------------------------- /APPFont/src/components/home/footer.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | 30 | -------------------------------------------------------------------------------- /APPFont/src/components/home/home.vue: -------------------------------------------------------------------------------- 1 | 20 | 36 | 48 | -------------------------------------------------------------------------------- /APPFont/src/components/home/introduce.vue: -------------------------------------------------------------------------------- 1 | 126 | 154 | 274 | -------------------------------------------------------------------------------- /APPFont/src/components/home/resource.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 23 | -------------------------------------------------------------------------------- /APPFont/src/components/home/topNavBar.vue: -------------------------------------------------------------------------------- 1 | 59 | 74 | 99 | -------------------------------------------------------------------------------- /APPFont/src/components/identify.vue: -------------------------------------------------------------------------------- 1 | 6 | 133 | -------------------------------------------------------------------------------- /APPFont/src/components/login.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 56 | 186 | 187 | 188 | 240 | -------------------------------------------------------------------------------- /APPFont/src/components/notFound.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 18 | -------------------------------------------------------------------------------- /APPFont/src/components/register.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 57 | 202 | 203 | 204 | 250 | -------------------------------------------------------------------------------- /APPFont/src/components/test.vue: -------------------------------------------------------------------------------- 1 | 20 | 94 | -------------------------------------------------------------------------------- /APPFont/src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App' 5 | //引入路由 6 | import router from './router' 7 | 8 | //引入elementUI 9 | import ElementUI from 'element-ui' 10 | import 'element-ui/lib/theme-chalk/index.css' 11 | //引入图标 12 | import './assets/icon/iconfont.css' 13 | import SIdentify from './components/identify' 14 | Vue.use(SIdentify); 15 | 16 | //安装插件 17 | Vue.use(ElementUI); 18 | 19 | //引入axios 20 | // import Axios from 'axios' 21 | // //设置全局URL 22 | // Axios.defaults.baseURL = 'http://127.0.0.1:8000/'; 23 | // Axios.defaults.headers.common["Authorization"] = '9a14fb9da6d2ffbf1d0e663d401e201b'; 24 | // // Axios.defaults.headers.common["Authorization"] = '7a88e6d7177a1360b06a3a437079c34d'; 25 | // Axios.defaults.headers.post['Content-Type']='application/x-www-form-urlencoded'; 26 | // Vue.prototype.$axios = Axios; 27 | 28 | 29 | 30 | 31 | import store from './store' 32 | 33 | 34 | 35 | 36 | 37 | //引入echarts 38 | import echarts from 'echarts' 39 | Vue.prototype.$echarts = echarts; 40 | 41 | 42 | // router.beforeEach((to, from, next) => { 43 | // next() 44 | // }); 45 | 46 | new Vue({ 47 | el: '#app', 48 | router, 49 | store, 50 | components: { App }, 51 | template: '' 52 | }); 53 | -------------------------------------------------------------------------------- /APPFont/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import store from '../store/index.js' 3 | import Router from 'vue-router' 4 | 5 | //路由懒加载 6 | 7 | const Home = resolve => { 8 | require.ensure(['../components/home/Home.vue'], () => { 9 | resolve(require('../components/home/Home.vue')); 10 | }); 11 | }; 12 | 13 | const Document = resolve => { 14 | require.ensure(['../components/home/document.vue'], () => { 15 | resolve(require('../components/home/document.vue')); 16 | }); 17 | }; 18 | 19 | const Introduce = resolve => { 20 | require.ensure(['../components/home/introduce.vue'], () => { 21 | resolve(require('../components/home/introduce.vue')); 22 | }); 23 | }; 24 | 25 | const Resource = resolve => { 26 | require.ensure(['../components/home/resource.vue'], () => { 27 | resolve(require('../components/home/resource.vue')); 28 | }); 29 | }; 30 | 31 | const Login = resolve => { 32 | require.ensure(['../components/login.vue'], () => { 33 | resolve(require('../components/login.vue')); 34 | }); 35 | }; 36 | 37 | const Register = resolve => { 38 | require.ensure(['../components/register.vue'], () => { 39 | resolve(require('../components/register')); 40 | }); 41 | }; 42 | 43 | const NotFound = resolve => { 44 | require.ensure(['../components/notFound.vue'], () => { 45 | resolve(require('../components/notFound.vue')); 46 | }); 47 | }; 48 | 49 | import Developer from '../components/developer/index' 50 | import Dashboard from '../components/developer/dashboard' 51 | import Charts from '../components/developer/charts' 52 | import Devices from '../components/developer/device' 53 | import Streams from '../components/developer/stream' 54 | import Triggers from '../components/developer/trigger' 55 | import Console from '../components/developer/console' 56 | import test from '../components/test' 57 | 58 | Vue.use(Router); 59 | 60 | const router = new Router({ 61 | mode:'history', 62 | routes: [ 63 | {path: '/', redirect: {path: '/home/introduce'}}, 64 | {path: '/home', redirect: {path: '/home/introduce'}}, 65 | {path: '/developer', redirect: {path: '/developer/dashboard'}}, 66 | { 67 | name: 'home', 68 | path: '/home', 69 | component: Home, 70 | children: [ 71 | {name: 'introduce', path: 'introduce', component: Introduce}, 72 | {name: 'document', path: 'document', component: Document}, 73 | {name: 'resource', path: 'resource', component: Resource}, 74 | ] 75 | }, 76 | { 77 | name: 'developer', 78 | path: '/developer', 79 | component: Developer, 80 | meta: { 81 | requiresAuth: true 82 | }, 83 | children: [ 84 | {name: 'dashboard', path: 'dashboard', component: Dashboard,meta: {requiresAuth: true}}, 85 | {name: 'charts', path: 'charts', component: Charts,meta: {requiresAuth: true}}, 86 | {name: 'devices', path: 'devices', component: Devices,meta: {requiresAuth: true}}, 87 | {name: 'streams', path: 'streams', component: Streams,meta: {requiresAuth: true}}, 88 | {name: 'console', path: 'console', component: Console,meta: {requiresAuth: true}}, 89 | {name: 'triggers', path: 'triggers', component: Triggers,meta: {requiresAuth: true}}, 90 | ] 91 | }, 92 | {name: 'login', path: '/login', component: Login}, 93 | {name: 'register', path: '/register', component: Register}, 94 | {name: 'test', path: '/test', component: test}, 95 | {path: '*', component: NotFound} 96 | ] 97 | }); 98 | 99 | router.beforeEach((to, from, next) => { 100 | //获取store里面的token 101 | let token = store.state.token; 102 | //判断要去的路由有没有requiresAuth 103 | if (to.meta.requiresAuth) { 104 | 105 | if (token) { 106 | next(); 107 | } else { 108 | next({ 109 | path: '/login', 110 | query: {redirect: to.fullPath} // 将刚刚要去的路由path(却无权限)作为参数,方便登录成功后直接跳转到该路由 111 | }); 112 | } 113 | 114 | } else { 115 | next();//如果无需token,那么随它去吧 116 | } 117 | }); 118 | export default router; 119 | 120 | -------------------------------------------------------------------------------- /APPFont/src/store/actions.js: -------------------------------------------------------------------------------- 1 | import * as types from './types.js' 2 | 3 | //actions常用于异步更改状态。也就是说它一般用来先发送请求,然后再commit 4 | 5 | //下面的代码可以说没必要。没发请求,多此一举。因为项目很久之前写的,这里我就不做大更改了,更改看下面 6 | //对于vuex不是很理解的可不看下面,先跟着我先的代码走一遍,之后思想明了,自己再做更改即可。写代码就是这样,刚开始难以写一手漂亮的代码。 7 | //直接在页面发送请求后再store.commit()是完全可以的 8 | export default { 9 | UserLogin({ commit }, data){ 10 | commit(types.LOGIN, data); 11 | }, 12 | UserLogout({ commit }){ 13 | commit(types.LOGOUT); 14 | }, 15 | UserName({ commit }, data){ 16 | commit(types.USERNAME, data); 17 | } 18 | } 19 | 20 | //改法: 21 | //举个例子:先引入api, 利用载荷拿到参数,然后发送登录请求 22 | // import api from '../axios.js'; 23 | // export default { 24 | // actions需要返回一个promise对象,是用于通知外面该actions执行完毕。(官网有写) 25 | // UserLogin({ commit }, { username, password }){ 26 | // 27 | // return new Promise((resolve, reject) => { 28 | // api.userLogin({ username, password}) 29 | // .then(res => { 30 | // //登录成功 31 | // if(res.data.success === true){ 32 | // let token = res.data.data.token; 33 | // commit(types.LOGIN, token); 34 | // resolve(res.data.success); 35 | // } 36 | // //登录失败: 用户名或密码错误 37 | // if(res.data.success === false){ 38 | // commit(types.LOGOUT); 39 | // resolve(res.data.success) 40 | // } 41 | // }) 42 | // .catch(err => { 43 | // reject(err); 44 | // }); 45 | // }); 46 | // 47 | // } 48 | // } 49 | -------------------------------------------------------------------------------- /APPFont/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | import mutations from './mutations.js' 5 | import actions from './actions.js' 6 | 7 | 8 | Vue.use(Vuex); 9 | 10 | //初始化时用sessionStore.getItem('token'),这样子刷新页面就无需重新登录 11 | const state = { 12 | token: window.sessionStorage.getItem('token'), 13 | username: '' 14 | }; 15 | 16 | export default new Vuex.Store({ 17 | state, 18 | mutations, 19 | actions 20 | }); -------------------------------------------------------------------------------- /APPFont/src/store/mutations.js: -------------------------------------------------------------------------------- 1 | import * as types from './types.js' 2 | 3 | //关于token的存放位置 4 | //1.只用vuex存储:刷新页面vuex重新初始化,token消失导致需要重新登录 5 | //2.window.sessionStorage: 初始化的时候用sessionStorage来赋值,刷新页面重新初始化,但因为sessionStorage里面存有token有值, 6 | //那么state.token就有值了,vue-router就不会拦截,则无需重新登录。(这个demo用的是这种) 7 | //3.window.localStorage: 初始化的时候用localStorage来赋值,这种情况跟sessionStorage差不多。后台配合给长时间有效的token比较好, 8 | //如果token的有效期比较短,直接有sessionStorage比较好。 9 | 10 | const mutations = { 11 | [types.LOGIN]: (state, data) => { 12 | //更改token的值 13 | state.token = data; 14 | window.sessionStorage.setItem('token', data); 15 | }, 16 | [types.LOGOUT]: (state) => { 17 | //登出的时候要清除token 18 | state.token = null; 19 | window.sessionStorage.removeItem('token'); 20 | }, 21 | //这步可有可无,根据自己需求去写 22 | [types.USERNAME]: (state, data) => { 23 | //把用户名存起来 24 | state.username = data; 25 | window.sessionStorage.setItem('username', data); 26 | } 27 | }; 28 | 29 | export default mutations; -------------------------------------------------------------------------------- /APPFont/src/store/types.js: -------------------------------------------------------------------------------- 1 | //常量用来给mutations用 2 | export const LOGIN = 'LOGIN'; 3 | 4 | export const LOGOUT = 'LOGOUT'; 5 | 6 | export const USERNAME = 'USERNAME'; -------------------------------------------------------------------------------- /APPFont/static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/APPFont/static/.gitkeep -------------------------------------------------------------------------------- /Hardware/NodeMCU_MQTT/NodeMCU_MQTT.ino: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | // Update these with values suitable for your network. 6 | 7 | const char* ssid = "JKGY-5F"; 8 | const char* password = "jkgy2017"; 9 | const char* mqtt_server = "123.207.87.193"; 10 | 11 | WiFiClient espClient; 12 | PubSubClient client(espClient); 13 | long lastMsg = 0; 14 | char msg[50]; 15 | int value = 0; 16 | int randNum; 17 | void setup() { 18 | pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output 19 | Serial.begin(115200); 20 | setup_wifi(); 21 | client.setServer(mqtt_server, 1883); 22 | client.setCallback(callback); 23 | } 24 | 25 | void setup_wifi() { 26 | 27 | delay(10); 28 | // We start by connecting to a WiFi network 29 | Serial.println(); 30 | Serial.print("Connecting to "); 31 | Serial.println(ssid); 32 | 33 | WiFi.begin(ssid, password); 34 | 35 | while (WiFi.status() != WL_CONNECTED) { 36 | delay(500); 37 | Serial.print("."); 38 | } 39 | 40 | Serial.println(""); 41 | Serial.println("WiFi connected"); 42 | Serial.println("IP address: "); 43 | Serial.println(WiFi.localIP()); 44 | } 45 | 46 | void callback(char* topic, byte* payload, unsigned int length) { 47 | Serial.print("Message arrived ["); 48 | Serial.print(topic); 49 | Serial.print("] "); 50 | for (int i = 0; i < length; i++) { 51 | Serial.print((char)payload[i]); 52 | } 53 | Serial.println(); 54 | 55 | // Switch on the LED if an 1 was received as first character 56 | if ((char)payload[0] == '1') { 57 | digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level 58 | // but actually the LED is on; this is because 59 | // it is acive low on the ESP-01) 60 | } else { 61 | digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH 62 | } 63 | 64 | } 65 | 66 | void reconnect() { 67 | // Loop until we're reconnected 68 | while (!client.connected()) { 69 | Serial.print("Attempting MQTT connection..."); 70 | // Attempt to connect 71 | if (client.connect("123456","123","123")) { 72 | Serial.println("connected"); 73 | client.publish("123456/temp1", "1"); 74 | } else { 75 | Serial.print("failed, rc="); 76 | Serial.print(client.state()); 77 | Serial.println(" try again in 5 seconds"); 78 | // Wait 5 seconds before retrying 79 | delay(5000); 80 | } 81 | } 82 | } 83 | void loop() { 84 | 85 | if (!client.connected()) { 86 | reconnect(); 87 | } 88 | client.loop(); 89 | 90 | long now = millis(); 91 | if (now - lastMsg > 2000) { 92 | lastMsg = now; 93 | ++value; 94 | snprintf (msg, 75, "%ld", value); 95 | Serial.print("Publish message: "); 96 | Serial.println(msg); 97 | client.publish("123456/temp1", msg); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /IOTPlatform/.idea/IOTPlatform.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 | 30 | -------------------------------------------------------------------------------- /IOTPlatform/.idea/dataSources.local.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #@ 7 | ` 8 | 9 | 10 | master_key 11 | root 12 | *:IOTPlatform 13 | 14 | 15 | -------------------------------------------------------------------------------- /IOTPlatform/.idea/dataSources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | mysql 6 | true 7 | com.mysql.jdbc.Driver 8 | jdbc:mysql://123.207.87.193:3306/IOTPlatform 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /IOTPlatform/.idea/dictionaries/fml.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | cloop 5 | cors 6 | corsheaders 7 | csrftoken 8 | keepalive 9 | mqtt 10 | 11 | 12 | -------------------------------------------------------------------------------- /IOTPlatform/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | -------------------------------------------------------------------------------- /IOTPlatform/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /IOTPlatform/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /IOTPlatform/IOTPlatform/__init__.py: -------------------------------------------------------------------------------- 1 | import pymysql 2 | # from utils.mqtt_client import MqClient 3 | 4 | pymysql.install_as_MySQLdb() 5 | 6 | -------------------------------------------------------------------------------- /IOTPlatform/IOTPlatform/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/IOTPlatform/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/IOTPlatform/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/IOTPlatform/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/IOTPlatform/__pycache__/settings.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/IOTPlatform/__pycache__/settings.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/IOTPlatform/__pycache__/settings.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/IOTPlatform/__pycache__/settings.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/IOTPlatform/__pycache__/urls.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/IOTPlatform/__pycache__/urls.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/IOTPlatform/__pycache__/urls.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/IOTPlatform/__pycache__/urls.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/IOTPlatform/__pycache__/wsgi.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/IOTPlatform/__pycache__/wsgi.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/IOTPlatform/__pycache__/wsgi.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/IOTPlatform/__pycache__/wsgi.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/IOTPlatform/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for IOTPlatform project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.1.2. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.1/ref/settings/ 11 | """ 12 | 13 | 14 | import os 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '8byztyq^ile$4-4#ph=r88ic)^a4ry^068yd5!p)dio+r0oi#l' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = ['*',] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = ( 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'corsheaders', 41 | 'rest_framework', 42 | 'website', 43 | 'weixin', 44 | ) 45 | 46 | MIDDLEWARE = [ 47 | 'django.middleware.security.SecurityMiddleware', 48 | 'django.contrib.sessions.middleware.SessionMiddleware', 49 | 'corsheaders.middleware.CorsMiddleware', 50 | 'django.middleware.common.CommonMiddleware', 51 | # 'django.middleware.csrf.CsrfViewMiddleware', 52 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 53 | 'django.contrib.messages.middleware.MessageMiddleware', 54 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 55 | ] 56 | 57 | ROOT_URLCONF = 'IOTPlatform.urls' 58 | 59 | TEMPLATES = [ 60 | { 61 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 62 | 'DIRS': [], 63 | 'APP_DIRS': True, 64 | 'OPTIONS': { 65 | 'context_processors': [ 66 | 'django.template.context_processors.debug', 67 | 'django.template.context_processors.request', 68 | 'django.contrib.auth.context_processors.auth', 69 | 'django.contrib.messages.context_processors.messages', 70 | ], 71 | }, 72 | }, 73 | ] 74 | 75 | WSGI_APPLICATION = 'IOTPlatform.wsgi.application' 76 | 77 | 78 | # Database 79 | # https://docs.djangoproject.com/en/2.1/ref/settings/#databases 80 | 81 | 82 | DATABASES = { 83 | 'default': { 84 | 'ENGINE': 'django.db.backends.mysql', 85 | 'NAME': 'IOTPlatform', 86 | 'USER': '***', 87 | 'PASSWORD': '****', 88 | 'HOST': '****', 89 | 'PORT': '3306', 90 | } 91 | } 92 | 93 | 94 | # Password validation 95 | # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators 96 | 97 | AUTH_PASSWORD_VALIDATORS = [ 98 | { 99 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 100 | }, 101 | { 102 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 103 | }, 104 | { 105 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 106 | }, 107 | { 108 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 109 | }, 110 | ] 111 | 112 | 113 | # Internationalization 114 | # https://docs.djangoproject.com/en/2.1/topics/i18n/ 115 | 116 | LANGUAGE_CODE = 'zh-Hans' 117 | 118 | TIME_ZONE = 'Asia/Shanghai' 119 | 120 | USE_I18N = True 121 | 122 | USE_L10N = True 123 | 124 | USE_TZ = False 125 | 126 | # Static files (CSS, JavaScript, Images) 127 | # https://docs.djangoproject.com/en/2.1/howto/static-files/ 128 | 129 | 130 | 131 | STATIC_URL = '/static/' 132 | 133 | TEMPLATE_DIRS = (os.path.join(BASE_DIR, 'templates'),) 134 | 135 | 136 | # cors跨域设置 137 | CORS_ALLOW_CREDENTIALS = True 138 | CORS_ORIGIN_ALLOW_ALL = True 139 | CORS_ORIGIN_WHITELIST = ( 140 | "*" 141 | ) 142 | CORS_ALLOW_METHODS = ( 143 | 'DELETE', 144 | 'GET', 145 | 'OPTIONS', 146 | 'PATCH', 147 | 'POST', 148 | 'PUT', 149 | 'VIEW', 150 | ) 151 | 152 | CORS_ALLOW_HEADERS = ( 153 | 'XMLHttpRequest', 154 | 'X_FILENAME', 155 | 'accept-encoding', 156 | 'authorization', 157 | 'content-type', 158 | 'dnt', 159 | 'origin', 160 | 'user-agent', 161 | 'x-csrftoken', 162 | 'x-requested-with', 163 | 'Pragma', 164 | ) 165 | 166 | # rest_framework全局权限认证类 167 | REST_FRAMEWORK = { 168 | "DEFAULT_AUTHENTICATION_CLASSES": ["website.utils.auth.Authtication"] 169 | } 170 | 171 | -------------------------------------------------------------------------------- /IOTPlatform/IOTPlatform/urls.py: -------------------------------------------------------------------------------- 1 | """IOTPlatform URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path, include 18 | urlpatterns = [ 19 | path(r'', include('website.urls')), 20 | path(r'wx/', include('weixin.urls')), 21 | 22 | ] 23 | -------------------------------------------------------------------------------- /IOTPlatform/IOTPlatform/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for IOTPlatform project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'IOTPlatform.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /IOTPlatform/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == '__main__': 6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'IOTPlatform.settings') 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /IOTPlatform/middleware/__pycache__/authentication.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/middleware/__pycache__/authentication.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/middleware/authentication.py: -------------------------------------------------------------------------------- 1 | from django.utils.deprecation import MiddlewareMixin 2 | from django.shortcuts import render, HttpResponse, redirect 3 | from website.datebase import API 4 | 5 | 6 | class AuthMiddleware(MiddlewareMixin): 7 | ''' 8 | 用户登录验证中间件 9 | ''' 10 | def process_request(self, request): 11 | print(request.COOKIES.get('is_log', None)) 12 | print(request.COOKIES.get('username', None)) 13 | print(request.path) 14 | # print(API().get_user_aid(request.COOKIES.get('username'))) 15 | if request.path != '/oa/login': 16 | # 如果不是登录页面并且没有登录状态则跳转到登录界面 17 | if request.COOKIES.get('is_log', None): 18 | name = request.COOKIES.get('username',None) 19 | if name: 20 | # 获取权限列表中的url 21 | url = API().get_user_url(name) 22 | url.append('/oa/error') 23 | url.append('/oa/userapi') 24 | url.append('/oa/api') 25 | url.append('/oa/device') 26 | url.append('/oa/logout') 27 | url.append('/oa/login') 28 | url.append('/oa/input/') 29 | if url: 30 | if request.path in url: 31 | return 32 | else: 33 | return render(request, '../templates/website/bad_request.html') 34 | else: 35 | return redirect('/oa/login') 36 | else: 37 | return 38 | 39 | def process_view(self, request, callback, callback_args, callback_kwargs): 40 | pass 41 | 42 | def process_response(self, request): 43 | pass 44 | 45 | def process_exception(self, request, exception): 46 | pass 47 | 48 | def process_response(self, request, response): 49 | return response 50 | 51 | -------------------------------------------------------------------------------- /IOTPlatform/utils/__pycache__/models.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/utils/__pycache__/models.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/utils/__pycache__/time_tools.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/utils/__pycache__/time_tools.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/utils/database.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine 2 | from sqlalchemy.orm import sessionmaker 3 | from models import * # 导入生成的model 4 | 5 | 6 | class DB(object): 7 | def __init__(self): 8 | engine = create_engine('mysql+pymysql://root:fml950826cs@123.207.87.193:3306/IOTPlatform') 9 | Session = sessionmaker(bind=engine) 10 | self.session = Session() 11 | 12 | 13 | def update_dev_stus(self, device_id, status): 14 | """ 15 | 更改设备状态 16 | :param device_id: 设备ID 17 | :param status: 状态 18 | :return: None 19 | """ 20 | try: 21 | self.session.query(WebsiteDevice).filter(WebsiteDevice.device_id==device_id).update({WebsiteDevice.dev_status:status}) 22 | self.session.flush() 23 | self.session.commit() 24 | return True 25 | except Exception as e: 26 | return False 27 | 28 | 29 | 30 | # db = DB() 31 | # db.update_dev_stus(123456,0) -------------------------------------------------------------------------------- /IOTPlatform/utils/models.py: -------------------------------------------------------------------------------- 1 | # pip install sqlacodegen 2 | # sqlacodegen mysql+pymysql://root:fml950826cs@123.207.87.193:3306/IOTPlatform > models.py 3 | 4 | 5 | # coding: utf-8 6 | from sqlalchemy import Column, ForeignKey, Index, String 7 | from sqlalchemy.dialects.mysql import DATETIME, INTEGER, LONGTEXT, SMALLINT, TINYINT, VARCHAR 8 | from sqlalchemy.orm import relationship 9 | from sqlalchemy.ext.declarative import declarative_base 10 | 11 | Base = declarative_base() 12 | metadata = Base.metadata 13 | 14 | 15 | class AuthGroup(Base): 16 | __tablename__ = 'auth_group' 17 | 18 | id = Column(INTEGER(11), primary_key=True) 19 | name = Column(String(80), nullable=False, unique=True) 20 | 21 | 22 | class AuthUser(Base): 23 | __tablename__ = 'auth_user' 24 | 25 | id = Column(INTEGER(11), primary_key=True) 26 | password = Column(String(128), nullable=False) 27 | last_login = Column(DATETIME(fsp=6)) 28 | is_superuser = Column(TINYINT(1), nullable=False) 29 | username = Column(String(150), nullable=False, unique=True) 30 | first_name = Column(String(30), nullable=False) 31 | last_name = Column(String(150), nullable=False) 32 | email = Column(String(254), nullable=False) 33 | is_staff = Column(TINYINT(1), nullable=False) 34 | is_active = Column(TINYINT(1), nullable=False) 35 | date_joined = Column(DATETIME(fsp=6), nullable=False) 36 | 37 | 38 | class DjangoContentType(Base): 39 | __tablename__ = 'django_content_type' 40 | __table_args__ = ( 41 | Index('django_content_type_app_label_model_76bd3d3b_uniq', 'app_label', 'model', unique=True), 42 | ) 43 | 44 | id = Column(INTEGER(11), primary_key=True) 45 | app_label = Column(String(100), nullable=False) 46 | model = Column(String(100), nullable=False) 47 | 48 | 49 | class DjangoMigration(Base): 50 | __tablename__ = 'django_migrations' 51 | 52 | id = Column(INTEGER(11), primary_key=True) 53 | app = Column(String(255), nullable=False) 54 | name = Column(String(255), nullable=False) 55 | applied = Column(DATETIME(fsp=6), nullable=False) 56 | 57 | 58 | class DjangoSession(Base): 59 | __tablename__ = 'django_session' 60 | 61 | session_key = Column(String(40), primary_key=True) 62 | session_data = Column(LONGTEXT, nullable=False) 63 | expire_date = Column(DATETIME(fsp=6), nullable=False, index=True) 64 | 65 | 66 | class WebsiteDatum(Base): 67 | __tablename__ = 'website_data' 68 | 69 | id = Column(INTEGER(11), primary_key=True) 70 | data = Column(String(10), nullable=False) 71 | date = Column(DATETIME(fsp=6), nullable=False) 72 | 73 | 74 | class WebsiteDatastream(Base): 75 | __tablename__ = 'website_datastream' 76 | 77 | id = Column(INTEGER(11), primary_key=True) 78 | name = Column(VARCHAR(32), nullable=False) 79 | min = Column(INTEGER(11)) 80 | max = Column(INTEGER(11)) 81 | qos = Column(INTEGER(11), nullable=False) 82 | unit = Column(VARCHAR(32)) 83 | unit_symbol = Column(VARCHAR(32)) 84 | trigger = Column(TINYINT(1), nullable=False) 85 | 86 | 87 | class WebsiteDevice(Base): 88 | __tablename__ = 'website_device' 89 | 90 | id = Column(INTEGER(11), primary_key=True) 91 | device_id = Column(INTEGER(11), nullable=False, unique=True) 92 | device_key = Column(String(32)) 93 | device_name = Column(VARCHAR(32), nullable=False) 94 | dev_status = Column(TINYINT(1), nullable=False) 95 | apiKey = Column(String(32)) 96 | date = Column(DATETIME(fsp=6), nullable=False) 97 | dev_introduce = Column(LONGTEXT) 98 | tag = Column(VARCHAR(32)) 99 | 100 | 101 | class WebsiteUserinfo(Base): 102 | __tablename__ = 'website_userinfo' 103 | 104 | id = Column(INTEGER(11), primary_key=True) 105 | username = Column(VARCHAR(32), nullable=False, unique=True) 106 | user_id = Column(INTEGER(11), nullable=False, unique=True) 107 | password = Column(VARCHAR(64), nullable=False) 108 | weixin_id = Column(VARCHAR(64), index=True) 109 | sex = Column(VARCHAR(16)) 110 | birthday = Column(VARCHAR(64)) 111 | tel = Column(VARCHAR(64)) 112 | email = Column(VARCHAR(64)) 113 | address = Column(VARCHAR(64)) 114 | introduction = Column(VARCHAR(255)) 115 | token = Column(VARCHAR(255)) 116 | 117 | 118 | class AuthPermission(Base): 119 | __tablename__ = 'auth_permission' 120 | __table_args__ = ( 121 | Index('auth_permission_content_type_id_codename_01ab375a_uniq', 'content_type_id', 'codename', unique=True), 122 | ) 123 | 124 | id = Column(INTEGER(11), primary_key=True) 125 | name = Column(String(255), nullable=False) 126 | content_type_id = Column(ForeignKey('django_content_type.id'), nullable=False) 127 | codename = Column(String(100), nullable=False) 128 | 129 | content_type = relationship('DjangoContentType') 130 | 131 | 132 | class AuthUserGroup(Base): 133 | __tablename__ = 'auth_user_groups' 134 | __table_args__ = ( 135 | Index('auth_user_groups_user_id_group_id_94350c0c_uniq', 'user_id', 'group_id', unique=True), 136 | ) 137 | 138 | id = Column(INTEGER(11), primary_key=True) 139 | user_id = Column(ForeignKey('auth_user.id'), nullable=False) 140 | group_id = Column(ForeignKey('auth_group.id'), nullable=False, index=True) 141 | 142 | group = relationship('AuthGroup') 143 | user = relationship('AuthUser') 144 | 145 | 146 | class DjangoAdminLog(Base): 147 | __tablename__ = 'django_admin_log' 148 | 149 | id = Column(INTEGER(11), primary_key=True) 150 | action_time = Column(DATETIME(fsp=6), nullable=False) 151 | object_id = Column(LONGTEXT) 152 | object_repr = Column(String(200), nullable=False) 153 | action_flag = Column(SMALLINT(5), nullable=False) 154 | change_message = Column(LONGTEXT, nullable=False) 155 | content_type_id = Column(ForeignKey('django_content_type.id'), index=True) 156 | user_id = Column(ForeignKey('auth_user.id'), nullable=False, index=True) 157 | 158 | content_type = relationship('DjangoContentType') 159 | user = relationship('AuthUser') 160 | 161 | 162 | class WebsiteDatastreamDatum(Base): 163 | __tablename__ = 'website_datastream_data' 164 | __table_args__ = ( 165 | Index('website_datastream_data_datastream_id_data_id_3b3216eb_uniq', 'datastream_id', 'data_id', unique=True), 166 | ) 167 | 168 | id = Column(INTEGER(11), primary_key=True) 169 | datastream_id = Column(ForeignKey('website_datastream.id'), nullable=False) 170 | data_id = Column(ForeignKey('website_data.id'), nullable=False, index=True) 171 | 172 | data = relationship('WebsiteDatum') 173 | datastream = relationship('WebsiteDatastream') 174 | 175 | 176 | class WebsiteDeviceDevStream(Base): 177 | __tablename__ = 'website_device_dev_stream' 178 | __table_args__ = ( 179 | Index('website_device_dev_stream_device_id_datastream_id_6d32e5c0_uniq', 'device_id', 'datastream_id', unique=True), 180 | ) 181 | 182 | id = Column(INTEGER(11), primary_key=True) 183 | device_id = Column(ForeignKey('website_device.id'), nullable=False) 184 | datastream_id = Column(ForeignKey('website_datastream.id'), nullable=False, index=True) 185 | 186 | datastream = relationship('WebsiteDatastream') 187 | device = relationship('WebsiteDevice') 188 | 189 | 190 | class WebsiteUserinfoDevice(Base): 191 | __tablename__ = 'website_userinfo_device' 192 | __table_args__ = ( 193 | Index('website_userinfo_device_userinfo_id_device_id_e34001e8_uniq', 'userinfo_id', 'device_id', unique=True), 194 | ) 195 | 196 | id = Column(INTEGER(11), primary_key=True) 197 | userinfo_id = Column(ForeignKey('website_userinfo.id'), nullable=False) 198 | device_id = Column(ForeignKey('website_device.id'), nullable=False, index=True) 199 | 200 | device = relationship('WebsiteDevice') 201 | userinfo = relationship('WebsiteUserinfo') 202 | 203 | 204 | class AuthGroupPermission(Base): 205 | __tablename__ = 'auth_group_permissions' 206 | __table_args__ = ( 207 | Index('auth_group_permissions_group_id_permission_id_0cd325b0_uniq', 'group_id', 'permission_id', unique=True), 208 | ) 209 | 210 | id = Column(INTEGER(11), primary_key=True) 211 | group_id = Column(ForeignKey('auth_group.id'), nullable=False) 212 | permission_id = Column(ForeignKey('auth_permission.id'), nullable=False, index=True) 213 | 214 | group = relationship('AuthGroup') 215 | permission = relationship('AuthPermission') 216 | 217 | 218 | class AuthUserUserPermission(Base): 219 | __tablename__ = 'auth_user_user_permissions' 220 | __table_args__ = ( 221 | Index('auth_user_user_permissions_user_id_permission_id_14a6b632_uniq', 'user_id', 'permission_id', unique=True), 222 | ) 223 | 224 | id = Column(INTEGER(11), primary_key=True) 225 | user_id = Column(ForeignKey('auth_user.id'), nullable=False) 226 | permission_id = Column(ForeignKey('auth_permission.id'), nullable=False, index=True) 227 | 228 | permission = relationship('AuthPermission') 229 | user = relationship('AuthUser') 230 | -------------------------------------------------------------------------------- /IOTPlatform/utils/mqtt_client.py: -------------------------------------------------------------------------------- 1 | import paho.mqtt.client as client 2 | import threading 3 | from database import DB 4 | 5 | class MqClient(object): 6 | def __init__(self, client_id, username, password): 7 | self.client = client.Client(client_id=client_id, 8 | clean_session=True) # 初始化,clean_session为false的时候EMQ会保存订阅状态,可以不再次订阅 9 | self.client.username_pw_set(username, password) # 设置连接用户名 10 | self.client.on_connect = self.on_connect 11 | self.client.on_message = self.on_message 12 | self._client_status = False # 连接状态 13 | self._cloop = None 14 | self._connect() # 实例化会自动连接 15 | self.db = DB() 16 | 17 | def _connect(self, host="123.207.87.193", port=1883, keepalive=60): 18 | """连接服务器""" 19 | self.client.connect_async(host, port, keepalive) 20 | # self.client.loop_forever() 21 | # 开启线程执行 22 | self._cloop = threading.Thread(target=self.client.loop_start()) 23 | self._cloop.start() 24 | 25 | def on_connect(self, client, userdata, flags, rc): 26 | """连接成功的回调函数""" 27 | # 订阅上下线消息 28 | self.client.subscribe("$SYS/brokers/emq@127.0.0.1/clients/#") 29 | # 修改客户端状态 30 | if rc == 0: 31 | self._client_status = True 32 | 33 | def init_sub(self): 34 | 35 | # 读取数据库中所有的已经注册过的topic并且订阅 36 | # for i in models.Device.objects.all(): 37 | # for j in i.dev_stream.all(): 38 | # self.client.subscribe(str(i.device_id) + '/' + j.name, j.qos) 39 | pass 40 | 41 | def on_message(self,client, userdata, msg): 42 | topic = msg.topic.split('/') 43 | client = topic[0] 44 | # 系统主题 45 | if client=='$SYS': 46 | status = 1 if topic[-1] == 'connected' else 0 47 | device_id = topic[-2] 48 | print(status,device_id) 49 | # 修改设备在线状态 50 | print(self.db.update_dev_stus(device_id, status)) 51 | # print(status,id) 52 | # print(models.Device.objects.filter(device_id=id).update(dev_status=status)) 53 | 54 | # else: 55 | # stream = topic[1] 56 | # data = msg.payload.decode() 57 | # # print(client_id,data_stream,data) 58 | # # 接收订阅信息写入到数据库中 59 | # models.DataStream.objects.filter(device__device_id=client).filter(name=stream).first().data.add( 60 | # models.Data.objects.create(data=data)) 61 | # pass 62 | 63 | c = MqClient('System', '123', '123') 64 | while True: 65 | pass 66 | 67 | -------------------------------------------------------------------------------- /IOTPlatform/utils/time_tools.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timedelta 2 | from calendar import monthrange 3 | 4 | 5 | def time_list(start_time, end_time): 6 | """ 7 | 根据起始时间和截至时间生成一个这个时间段的时间列表 8 | :param start_time: 起始时间 YYYY-MM-DD 9 | :param end_time: 终止时间 YYYY-MM-DD 10 | :return: list ['YYYY-MM-DD',.....] 11 | """ 12 | dates = [] 13 | dt = datetime.strptime(start_time, "%Y-%m-%d") 14 | date = start_time[:] 15 | while date <= end_time: 16 | dates.append(date) 17 | dt += timedelta(days=1) 18 | date = dt.strftime("%Y-%m-%d") 19 | dates.reverse() 20 | return dates 21 | 22 | 23 | def day(time, days, method=1): 24 | """ 25 | 返回起始时间之前或之后的日期(默认之前) 26 | :param time: 截止日期 YYYY-MM-DD 27 | :param days: 天数 28 | :param method: 大于0返回起始日期之前的时间,小于0返回起始日期之后的时间 29 | :return: str 'YYYY-MM-DD' 30 | """ 31 | dt = datetime.strptime(time, "%Y-%m-%d") 32 | if method > 0: 33 | dt -= timedelta(days=days) 34 | else: 35 | dt += timedelta(days=days) 36 | date = dt.strftime("%Y-%m-%d") 37 | return date 38 | 39 | 40 | def apart(start_time, end_time): 41 | """ 42 | 返回两个时间段相隔天数 43 | :param start_time: 起始时间 YYYY-MM-DD 44 | :param end_time: 终止时间 YYYY-MM-DD 45 | :return: 相隔天数 int 46 | """ 47 | start_dt = datetime.strptime(start_time, "%Y-%m-%d") 48 | end_dt = datetime.strptime(end_time, "%Y-%m-%d") 49 | return (end_dt - start_dt).days 50 | 51 | 52 | def month(now, month): 53 | """ 54 | 获取前几个月或后几个月第一天和最后一天 55 | :param now: 当前时间 56 | :param month: 所隔月数,大于0 返回之前月份,小于0返回之后月份 57 | :return: list [start,end] 58 | """ 59 | dt = datetime.strptime(now, "%Y-%m-%d") 60 | start = datetime(dt.year, dt.month-month, 1).strftime("%Y-%m-%d") 61 | end = datetime(dt.year, dt.month-month, monthrange(dt.year, dt.month-month)[1]).strftime("%Y-%m-%d") 62 | return [start, end] 63 | -------------------------------------------------------------------------------- /IOTPlatform/website/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /IOTPlatform/website/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/__pycache__/admin.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/__pycache__/admin.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/__pycache__/admin.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/__pycache__/admin.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/__pycache__/console.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/__pycache__/console.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/__pycache__/database.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/__pycache__/database.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/__pycache__/database.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/__pycache__/database.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/__pycache__/models.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/__pycache__/models.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/__pycache__/models.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/__pycache__/models.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/__pycache__/urls.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/__pycache__/urls.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/__pycache__/urls.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/__pycache__/urls.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/__pycache__/views.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/__pycache__/views.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/__pycache__/views.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/__pycache__/views.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /IOTPlatform/website/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class WebsiteConfig(AppConfig): 5 | name = 'website' 6 | -------------------------------------------------------------------------------- /IOTPlatform/website/console.py: -------------------------------------------------------------------------------- 1 | import requests,json 2 | from requests.auth import HTTPBasicAuth 3 | 4 | 5 | def send(data): 6 | """ 7 | 调用EMQ http接口发送消息 8 | :param data: 消息内容 dict 9 | :return: dict 10 | """ 11 | try: 12 | data = json.dumps(data) 13 | auth = HTTPBasicAuth('admin', 'fml950826cs') # basicAuth认证 14 | res = requests.post(url="http://123.207.87.193:18083/api/v2/mqtt/publish", auth=auth,data=data) 15 | # success: {"code": 0, "result": []} 16 | if res.status_code==200: 17 | return json.loads(res.text) 18 | else: 19 | return {"code": 1, "result": []} 20 | except Exception as e: 21 | return {"code": 1, "result": []} 22 | 23 | 24 | 25 | # 测试 26 | # data = { 27 | # "topic" : "$client/123456", 28 | # "payload": "0", 29 | # "qos": 0, 30 | # "retain" : False, 31 | # "client_id": "mqttjs_b4b9e8e2c3" 32 | # } 33 | # data={'topic': '$client/123456', 'payload': '1', 'qos': 0} 34 | # print(send(data)) -------------------------------------------------------------------------------- /IOTPlatform/website/database.py: -------------------------------------------------------------------------------- 1 | from website import models 2 | from django.db import transaction 3 | from website.utils import create 4 | from utils.time_tools import time_list, day 5 | import time, datetime 6 | 7 | 8 | def login(username, password): 9 | """ 10 | 登录验证 11 | :param username: 用户名 12 | :param password: 密码 13 | :return: {'code': 0, 'message': "", 'data': {'token': token}} 14 | """ 15 | res = {'code': 0, 'message': "", 'data': {}} 16 | # 检查参数是否正确 17 | if not username or not password: 18 | res['code'] = 1 19 | res['message'] = '参数有误' 20 | return res 21 | try: 22 | # 获取用户对象 23 | userinfo = models.UserInfo.objects.filter(username=username).first() 24 | if userinfo: 25 | if password == userinfo.password: 26 | # 登陆成功生成token保存并返回(以后每次请求都需携带这个token验证) 27 | token = create.create_token(username) 28 | models.UserInfo.objects.filter(username=username).update(token=token) 29 | res['data']['token'] = token 30 | return res 31 | else: 32 | res['code'] = 2 33 | res['message'] = '密码错误' 34 | return res 35 | else: 36 | res['code'] = 3 37 | res['message'] = '当前用户不存在' 38 | return res 39 | except Exception as e: 40 | res['code'] = 1 41 | res['message'] = e.__repr__() 42 | return res 43 | 44 | 45 | def register(username, password, email): 46 | """ 47 | 注册用户 48 | :param username: 用户名(唯一的) 49 | :param password: 密码 50 | :param email: 邮箱 51 | :return: {'code': 0, 'message': '', 'data': {'user_id': ""}} 52 | """ 53 | res = {'code': 0, 'message': '', 'data': {}} 54 | # 检查参数是否正确 55 | if not username or not password or not email: 56 | res['code'] = 1 57 | res['message'] = '参数错误' 58 | return res 59 | # 检查用户名是否重复 60 | if models.UserInfo.objects.filter(username=username).first(): 61 | res['code'] = 2 62 | res['message'] = "用户名已存在" 63 | return res 64 | # 生成user_id , user_id是唯一的防止重复 65 | user_id = hash(username + str(time.time())) % 10 ** 9 66 | while models.UserInfo.objects.filter(user_id=user_id).first(): 67 | user_id = hash(username + str(time.time())) % 10 ** 9 68 | try: 69 | with transaction.atomic(): # 出错回滚 70 | models.UserInfo.objects.create(username=username, password=password, user_id=user_id, email=email) 71 | res['message'] = 'Succeed' 72 | res['data']['user_id'] = user_id 73 | except Exception as e: 74 | res['code'] = 3 75 | res['message'] = e.__repr__() 76 | return res 77 | 78 | 79 | def dashboard(username, start_time, end_time): 80 | res = {'code': 0, 'message': "", 81 | 'data': { 82 | 'card': {"dev_num": 0, "str_num": 0, "dp_num": 0, "trg_num": 0}, 83 | 'chart': {'title': [], 'point': []} 84 | } 85 | } 86 | try: 87 | if not start_time or not end_time: 88 | end_time = datetime.datetime.now().strftime("%Y-%m-%d") 89 | start_time = day(end_time, 10) 90 | date_list = time_list(start_time, end_time) 91 | date_list.reverse() 92 | res['data']['chart']['title'] = date_list 93 | res['data']['chart']['point'] = [0 for i in date_list] 94 | 95 | user_obj = models.UserInfo.objects.filter(username=username).first() 96 | res['data']['card']['dev_num'] = user_obj.device.count() 97 | # print(device.first().dev_stream.count()) 98 | for i in user_obj.device.all(): 99 | res['data']['card']['str_num'] += i.dev_stream.count() 100 | for j in i.dev_stream.all(): 101 | if j.trigger: 102 | res['data']['card']['trg_num'] += 1 103 | res['data']['card']['dp_num'] += j.data.count() 104 | for t in range(len(date_list)): 105 | res['data']['chart']['point'][t] += j.data.filter(date__startswith=date_list[t]).count() 106 | except Exception as e: 107 | res['code'] = 1 108 | res['message'] = e.__repr__() 109 | return res 110 | 111 | 112 | def charts(username, start_time, end_time): 113 | res = {'code': 0, 'message': "", 'data': {}} 114 | try: 115 | 116 | if not start_time or not end_time: 117 | end_time = datetime.datetime.now().strftime("%Y-%m-%d") 118 | start_time = day(end_time, 10) 119 | date_list = time_list(start_time, end_time) 120 | date_list.reverse() 121 | 122 | user_obj = models.UserInfo.objects.filter(username=username).first() 123 | for i in user_obj.device.all(): 124 | res['data'][i.device_name] = {} 125 | for j in i.dev_stream.all(): 126 | 127 | res['data'][i.device_name][j.name] = [[], []] 128 | for t in j.data.all(): 129 | res['data'][i.device_name][j.name][0].append(str(t.date)[:19]) 130 | res['data'][i.device_name][j.name][1].append(t.data) 131 | 132 | except Exception as e: 133 | res['code'] = 1 134 | res['message'] = e.__repr__() 135 | return res 136 | 137 | 138 | def get_device(username): 139 | res = {'code': 0, 'message': '', 'data': []} 140 | try: 141 | user_obj = models.UserInfo.objects.filter(username=username).first() # 获取用户对象 142 | if not user_obj: 143 | res['code'] = 1 144 | res['message'] = '无法找到用户信息' 145 | return res 146 | for i in user_obj.device.all(): # 遍历其下多有的设备信息 147 | buff = {'id': i.device_id, 'name': i.device_name, 'status': i.dev_status, 'create_time': str(i.date)[:16], 148 | 'introduce': i.dev_introduce, 'APIkey': i.apiKey, 'tag': i.tag, 'stream_num': i.dev_stream.count()} 149 | res['data'].append(buff) 150 | 151 | except Exception as e: 152 | res['code'] = 1 153 | res['message'] = e.__repr__() 154 | return res 155 | 156 | 157 | def update_device(username, id, name, introduce, tag): 158 | res = {'code': 0, 'message': "", 'data': {}} 159 | try: 160 | # 检查一下此用户下是否存在这个ID 的设备,防止其他人篡改 161 | dev_obj = models.UserInfo.objects.filter(username=username).device.filter(device_id=id) 162 | if dev_obj: 163 | dev_obj.update(device_name=name, tag=tag, dev_introduce=introduce) 164 | else: 165 | res['code'] = 1 166 | res['message'] = '抱歉你的账户下没有这个ID的设备,无法修改' 167 | except Exception as e: 168 | res['code'] = 1 169 | res['message'] = e.__repr__() 170 | return res 171 | 172 | 173 | def add_device(username, name, introduce, tag): 174 | res = {'code': 0, 'message': "", 'data': {}} 175 | try: 176 | # 生成唯一的设备ID 177 | device_id = hash(username + str(time.time())) % 10 ** 9 178 | while models.Device.objects.filter(device_id=device_id).first(): 179 | device_id = hash(username + str(time.time())) % 10 ** 9 180 | # 生成设备鉴权信息 181 | apiKey = create.create_token(str(device_id) + username) 182 | # 创建设备对象 183 | dev_obj = models.Device.objects.create(device_id=device_id, device_name=name, dev_introduce=introduce, tag=tag, 184 | apiKey=apiKey) 185 | # 将创建的设备和用户关联起来 186 | models.UserInfo.objects.filter(username=username).first().device.add(dev_obj) 187 | except Exception as e: 188 | res['code'] = 1 189 | print(e.__repr__()) 190 | res['message'] = e.__repr__() 191 | return res 192 | 193 | 194 | def del_device(username, id, apiKey): 195 | res = {'code': 0, 'message': "", 'data': {}} 196 | try: 197 | # 检查一下此用户下是否存在这个ID 的设备,防止其他人篡改 198 | dev_obj = models.UserInfo.objects.filter(username=username).first().device.filter(device_id=id) 199 | if dev_obj: 200 | # 这里再次判断设备鉴权信息是否正确 201 | obj = dev_obj.filter(apiKey=apiKey) 202 | if obj: 203 | for i in obj.first().dev_stream.all(): 204 | for d in i.data.all(): 205 | d.delete() # 删除数据点 206 | i.delete() # 删除数据流 207 | obj.delete() # 这里只是删除了关联,其下的数据流并没有删除, 所以还需要上面的两步删除干净 208 | 209 | else: 210 | res['code'] = 1 211 | res['message'] = '设备鉴权信息错误,无法删除' 212 | return res 213 | else: 214 | res['code'] = 1 215 | res['message'] = '抱歉你的账户下没有这个ID的设备,无法删除' 216 | return res 217 | except Exception as e: 218 | res['code'] = 1 219 | res['message'] = e.__repr__() 220 | return res 221 | 222 | 223 | def get_stream(username): 224 | res = {'code': 0, 'message': "", 'data': []} 225 | trigger_type = ['无','<','<=','==','>','>=','change','inout'] 226 | try: 227 | user_obj = models.UserInfo.objects.filter(username=username).first() 228 | if not user_obj: 229 | res['code'] = 1 230 | res['message'] = '无法找到用户信息' 231 | return res 232 | for i in user_obj.device.all(): # 遍历其下多有的设备信息 233 | for j in i.dev_stream.all(): # 再遍历每个设备下的数据流 234 | buff = {'name': j.name, 'dev_id': i.device_id, 'dev_name': i.device_name, 'unit': j.unit, 235 | 'unit_symbol': j.unit_symbol, 'trigger': '是' if j.trigger else '否', 'qos': j.qos, 'max': j.max, 236 | 'min': j.min, 'trigger_type':trigger_type[j.min]} 237 | res['data'].append(buff) 238 | except Exception as e: 239 | res['code'] = 1 240 | res['message'] = e.__repr__() 241 | return res 242 | 243 | 244 | def update_stream(username, dev_id, name, unit, unit_symbol, max, min, qos): 245 | res = {'code': 0, 'message': "", 'data': {}} 246 | try: 247 | dev_obj = models.UserInfo.objects.filter(username=username).first().device.filter(device_id=dev_id).first() 248 | if dev_obj: 249 | dev_obj.dev_stream.filter(name=name).update(unit=unit, unit_symbol=unit_symbol, max=max, min=min, qos=qos) 250 | else: 251 | res['code'] = 1 252 | res['message'] = '抱歉你的账户下没有这个ID的设备,无法修改' 253 | except Exception as e: 254 | res['code'] = 1 255 | res['message'] = e.__repr__() 256 | return res 257 | 258 | 259 | def add_stream(username, dev_id, name, unit, unit_symbol, max, min, qos): 260 | res = {'code': 0, 'message': "", 'data': {}} 261 | try: 262 | dev_obj = models.UserInfo.objects.filter(username=username).first().device.filter(device_id=dev_id).first() 263 | if dev_obj: 264 | stream_obj = models.DataStream.objects.create(name=name,unit=unit,unit_symbol=unit_symbol,max=max,min=min,qos=qos) 265 | dev_obj.dev_stream.add(stream_obj) 266 | else: 267 | res['code'] = 1 268 | res['message'] = '抱歉你的账户下没有这个ID的设备,无法新建此数据流' 269 | except Exception as e: 270 | res['code'] = 1 271 | res['message'] = e.__repr__() 272 | return res 273 | 274 | 275 | def del_stream(username, dev_id, name): 276 | res = {'code': 0, 'message': "", 'data': {}} 277 | try: 278 | dev_obj = models.UserInfo.objects.filter(username=username).first().device.filter(device_id=dev_id).first() 279 | if dev_obj: 280 | str_obj = dev_obj.dev_stream.filter(name=name) 281 | for d in str_obj.first().data.all(): 282 | d.delete() # 删除数据点 283 | str_obj.delete() # 删除数据流 284 | 285 | else: 286 | res['code'] = 1 287 | res['message'] = '抱歉你的账户下没有这个ID的设备,无法删除此数据流' 288 | except Exception as e: 289 | res['code'] = 1 290 | res['message'] = e.__repr__() 291 | return res 292 | 293 | 294 | def get_trigger(username): 295 | res = {'code': 0, 'message': "", 'data': []} 296 | try: 297 | user_obj = models.UserInfo.objects.filter(username=username).first() 298 | if not user_obj: 299 | res['code'] = 1 300 | res['message'] = '无法找到用户信息' 301 | return res 302 | for i in user_obj.device.all(): # 遍历其下多有的设备信息 303 | for j in i.dev_stream.all(): # 再遍历每个设备下的数据流 304 | if j.trigger: 305 | buff = {'name': j.name, 'dev_id': i.device_id, 'dev_name': i.device_name, 'unit': j.unit, 306 | 'unit_symbol': j.unit_symbol, 'trigger': '是' if j.trigger else '否', 'qos': j.qos, 'max': j.max, 307 | 'min': j.min} 308 | res['data'].append(buff) 309 | except Exception as e: 310 | res['code'] = 1 311 | res['message'] = e.__repr__() 312 | return res 313 | -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.2 on 2018-11-06 19:36 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Data', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('data', models.CharField(max_length=10)), 19 | ('date', models.DateTimeField(auto_now_add=True)), 20 | ], 21 | ), 22 | migrations.CreateModel( 23 | name='DataStream', 24 | fields=[ 25 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 26 | ('name', models.CharField(max_length=32)), 27 | ('min', models.IntegerField(null=True)), 28 | ('max', models.IntegerField(null=True)), 29 | ('qos', models.IntegerField(default=0)), 30 | ('unit', models.CharField(max_length=32)), 31 | ('unit_symbol', models.CharField(max_length=32)), 32 | ('data', models.ManyToManyField(to='website.Data')), 33 | ], 34 | ), 35 | migrations.CreateModel( 36 | name='Device', 37 | fields=[ 38 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 39 | ('device_id', models.IntegerField(unique=True)), 40 | ('device_key', models.CharField(max_length=32, null=True)), 41 | ('device_name', models.CharField(max_length=32)), 42 | ('dev_status', models.BooleanField(default=0)), 43 | ('dev_stream', models.ManyToManyField(to='website.DataStream')), 44 | ], 45 | ), 46 | migrations.CreateModel( 47 | name='UserInfo', 48 | fields=[ 49 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 50 | ('username', models.CharField(db_index=True, max_length=32, unique=True)), 51 | ('user_id', models.IntegerField(db_index=True, unique=True)), 52 | ('password', models.CharField(max_length=64)), 53 | ('weixin_id', models.CharField(db_index=True, max_length=64, null=True)), 54 | ('sex', models.CharField(max_length=16, null=True)), 55 | ('birthday', models.CharField(max_length=64, null=True)), 56 | ('tel', models.CharField(max_length=64, null=True)), 57 | ('email', models.CharField(max_length=64, null=True)), 58 | ('address', models.CharField(max_length=64, null=True)), 59 | ('introduction', models.CharField(max_length=255, null=True)), 60 | ('token', models.CharField(max_length=255, null=True)), 61 | ('device', models.ManyToManyField(to='website.Device')), 62 | ], 63 | ), 64 | ] 65 | -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/0002_auto_20181110_0221.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.2 on 2018-11-10 02:21 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('website', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | 14 | migrations.AlterField( 15 | model_name='datastream', 16 | name='unit', 17 | field=models.CharField(max_length=32, null=True), 18 | ), 19 | migrations.AlterField( 20 | model_name='datastream', 21 | name='unit_symbol', 22 | field=models.CharField(max_length=32, null=True), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/0003_auto_20181114_0143.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.2 on 2018-11-14 01:43 2 | 3 | from django.db import migrations, models 4 | import django.utils.timezone 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('website', '0002_auto_20181110_0221'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='device', 16 | name='apiKey', 17 | field=models.CharField(max_length=32, null=True), 18 | ), 19 | migrations.AddField( 20 | model_name='device', 21 | name='date', 22 | field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), 23 | preserve_default=False, 24 | ), 25 | migrations.AddField( 26 | model_name='device', 27 | name='dev_introduce', 28 | field=models.TextField(null=True), 29 | ), 30 | migrations.AddField( 31 | model_name='device', 32 | name='tag', 33 | field=models.CharField(max_length=32, null=True), 34 | ), 35 | ] 36 | -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/0004_auto_20181114_0144.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.2 on 2018-11-14 01:44 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('website', '0003_auto_20181114_0143'), 10 | ] 11 | 12 | operations = [ 13 | 14 | ] 15 | -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/0005_auto_20181114_0148.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.2 on 2018-11-14 01:48 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('website', '0004_auto_20181114_0144'), 10 | ] 11 | 12 | operations = [ 13 | 14 | ] -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/0006_auto_20181118_1522.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.2 on 2018-11-18 15:22 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('website', '0005_auto_20181114_0148'), 10 | ] 11 | 12 | operations = [ 13 | 14 | migrations.AlterField( 15 | model_name='datastream', 16 | name='max', 17 | field=models.IntegerField(default=0, null=True), 18 | ), 19 | migrations.AlterField( 20 | model_name='datastream', 21 | name='min', 22 | field=models.IntegerField(default=0, null=True), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/0007_auto_20181118_1618.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.2 on 2018-11-18 16:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('website', '0006_auto_20181118_1522'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='datastream', 15 | name='qos', 16 | field=models.IntegerField(default=0, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | 2 -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/__pycache__/0001_initial.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/migrations/__pycache__/0001_initial.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/__pycache__/0001_initial.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/migrations/__pycache__/0001_initial.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/__pycache__/0002_auto_20181110_0221.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/migrations/__pycache__/0002_auto_20181110_0221.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/__pycache__/0003_auto_20181114_0143.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/migrations/__pycache__/0003_auto_20181114_0143.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/__pycache__/0004_auto_20181114_0144.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/migrations/__pycache__/0004_auto_20181114_0144.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/__pycache__/0005_auto_20181114_0148.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/migrations/__pycache__/0005_auto_20181114_0148.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/__pycache__/0006_auto_20181118_1522.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/migrations/__pycache__/0006_auto_20181118_1522.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/__pycache__/0007_auto_20181118_1618.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/migrations/__pycache__/0007_auto_20181118_1618.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/migrations/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/migrations/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/migrations/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Data(models.Model): 5 | data = models.CharField(max_length=10) # 限制在两位小数 6 | date = models.DateTimeField(auto_now_add=True) # 上传时间,自动保存时间 7 | 8 | 9 | class DataStream(models.Model): 10 | name = models.CharField(max_length=32, null=False) # 数据流名称 11 | min = models.IntegerField(null=True, default=0) # 判断类型 0: None 1: < 2:<= 3:== 4:> 5:>= 6:change 7:inout 12 | max = models.IntegerField(null=True, default=0) # 阈值数值 13 | qos = models.IntegerField(null=True, default=0) # 服务质量 14 | unit = models.CharField(max_length=32, null=True) # 单位名称 15 | unit_symbol = models.CharField(max_length=32, null=True) # 单位符号 16 | trigger = models.BooleanField(default=0) # 触发器 0:不需要 1:微信 2:邮箱 这两个都是使用userinfo表中绑定的信息 其他http请求(写入的时候需要需要验证URL格式) 17 | data = models.ManyToManyField(Data) 18 | 19 | 20 | class Device(models.Model): 21 | device_id = models.IntegerField(unique=True, null=False) # 设备号 22 | device_key = models.CharField(max_length=32, null=True) # 设备token 23 | device_name = models.CharField(max_length=32) # 设备名 24 | dev_status = models.BooleanField(default=0) # 在线状态 25 | dev_introduce = models.TextField(null=True) # 设备简介 26 | date = models.DateTimeField(auto_now_add=True) # 创建时间,自动保存时间 27 | tag = models.CharField(max_length=32,null=True) # 设备标签 28 | apiKey = models.CharField(max_length=32, null=True) # apiKey 29 | dev_stream = models.ManyToManyField(DataStream) # 传感器数据流 30 | 31 | 32 | class UserInfo(models.Model): 33 | username = models.CharField(max_length=32, null=False, unique=True, db_index=True) # 用户名 34 | user_id = models.IntegerField(unique=True, null=False, db_index=True) # 用户ID 35 | password = models.CharField(max_length=64, null=False) # 密码 36 | weixin_id = models.CharField(max_length=64, null=True, db_index=True) # 微信号 37 | # 个人信息 38 | sex = models.CharField(max_length=16, null=True) # 性别 39 | birthday = models.CharField(max_length=64, null=True) # 生日 40 | tel = models.CharField(max_length=64, null=True) # 联系方式 41 | email = models.CharField(max_length=64, null=True) # 邮箱 42 | address = models.CharField(max_length=64, null=True) # 地址 43 | introduction = models.CharField(max_length=255, null=True) # 个人简介 44 | token = models.CharField(max_length=255, null=True) 45 | # 绑定设备 46 | device = models.ManyToManyField(Device) 47 | -------------------------------------------------------------------------------- /IOTPlatform/website/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /IOTPlatform/website/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from website import views 3 | urlpatterns = [ 4 | url(r'^login$', views.Login.as_view()), # 登陆验证 5 | url(r'^register$', views.Register.as_view()), # 登陆验证 6 | url(r'^dashboard$', views.Dashboard.as_view()), # 登陆验证 7 | url(r'^charts$', views.Chart.as_view()), # 登陆验证 8 | url(r'^devices', views.Device.as_view()), # 登陆验证 9 | url(r'^streams', views.Stream.as_view()), # 登陆验证 10 | url(r'^console', views.Console.as_view()), # 登陆验证 11 | url(r'^triggers', views.Trigger.as_view()), # 登陆验证 12 | 13 | ] 14 | -------------------------------------------------------------------------------- /IOTPlatform/website/utils/__pycache__/auth.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/utils/__pycache__/auth.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/utils/__pycache__/create.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/utils/__pycache__/create.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/utils/__pycache__/create.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/website/utils/__pycache__/create.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/website/utils/auth.py: -------------------------------------------------------------------------------- 1 | from rest_framework.authentication import BaseAuthentication 2 | from rest_framework import exceptions 3 | from website import models 4 | 5 | 6 | class Authtication(BaseAuthentication): 7 | def authenticate(self, request): # 如果执行到最后一个还是没有给user赋值,则会返回一个匿名用户 8 | token = request._request.META.get('HTTP_AUTHORIZATION') 9 | obj = models.UserInfo.objects.filter(token=token).first() 10 | if not token or not obj: 11 | raise exceptions.AuthenticationFailed({'code': 1, 'message': '没有登录!', 'data': {}}) 12 | return obj.username, None 13 | 14 | -------------------------------------------------------------------------------- /IOTPlatform/website/utils/create.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import time 3 | 4 | def create_dev_id(username): 5 | """ 6 | 生成dev_id 7 | :param username: 用户名 8 | :return: dev_id 9 | """ 10 | ctime = str(time.time()) 11 | m = hashlib.md5(bytes(username, encoding='utf-8')) 12 | m.update(bytes(ctime, encoding='utf-8')) 13 | return m.hexdigest() 14 | 15 | def create_token(username): 16 | """ 17 | 生成token,用户登录成功后返回token,下次访问需要携带token验证用户信息 18 | :param username: 用户名 19 | :return: token 20 | """ 21 | ctime = str(time.time()) 22 | m = hashlib.md5(bytes(username, encoding='utf-8')) 23 | m.update(bytes(ctime, encoding='utf-8')) 24 | return m.hexdigest() -------------------------------------------------------------------------------- /IOTPlatform/website/views.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | # from django.shortcuts import render, HttpResponse 3 | from rest_framework.views import APIView 4 | from rest_framework.parsers import JSONParser, FormParser 5 | from website import database 6 | from website.console import send 7 | 8 | error_msg = {'code': 100, 'message': 'request parameter error'} 9 | 10 | 11 | class Login(APIView): 12 | parser_classes = [JSONParser, FormParser] 13 | authentication_classes = [] 14 | 15 | def post(self, request): 16 | username = request.data.get('username', None) 17 | password = request.data.get('password', None) 18 | res = database.login(username, password) 19 | return JsonResponse(res) 20 | 21 | 22 | class Register(APIView): 23 | authentication_classes = [] 24 | 25 | def post(self, request): 26 | username = request.POST.get('username', None) 27 | password = request.POST.get('password', None) 28 | email = request.POST.get('email', None) 29 | res = database.register(username, password, email) 30 | return JsonResponse(res) 31 | 32 | 33 | class Dashboard(APIView): 34 | def get(self, request): 35 | res = database.dashboard(request.user, None, None) 36 | return JsonResponse(res) 37 | 38 | 39 | class Chart(APIView): 40 | def get(self, request): 41 | res = database.charts(request.user, None, None) 42 | return JsonResponse(res) 43 | 44 | 45 | class Device(APIView): 46 | parser_classes = [JSONParser, FormParser] 47 | 48 | def get(self, request): 49 | """获取设备""" 50 | res = database.get_device(request.user) 51 | return JsonResponse(res) 52 | 53 | def post(self, request): 54 | """新建设备""" 55 | name = request.data.get('name', None) 56 | introduce = request.data.get('introduce', None) 57 | tag = request.data.get('tag', None) 58 | # print(name,introduce,tag) 59 | res = database.add_device(request.user, name, introduce, tag) 60 | 61 | return JsonResponse(res) 62 | 63 | def put(self, request): 64 | """修改设备信息""" 65 | id = request.data.get('id', None) 66 | name = request.data.get('name', None) 67 | introduce = request.data.get('introduce', None) 68 | tag = request.data.get('tag', None) 69 | res = database.update_device(request.user, id, name, introduce, tag) 70 | return JsonResponse(res) 71 | 72 | def delete(self, request): 73 | id = request.data.get('id', None) 74 | apiKey = request.data.get('apiKey', None) 75 | res = database.del_device(request.user, id, apiKey) 76 | return JsonResponse(res) 77 | 78 | 79 | class Stream(APIView): 80 | parser_classes = [JSONParser, FormParser] 81 | 82 | def get(self, request): 83 | """获取设备""" 84 | res = database.get_stream(request.user) 85 | return JsonResponse(res) 86 | 87 | def post(self, request): 88 | """ 89 | 新建数据流 90 | :param request: name, dev_id, qos, min 都不能为空 91 | :return: 92 | """ 93 | name = request.data.get('name', None) 94 | unit = request.data.get('unit', None) 95 | unit_symbol = request.data.get('unit_symbol', None) 96 | qos = request.data.get('qos', 0) 97 | dev_id = request.data.get('dev_id', None) 98 | max = request.data.get('max', 0) 99 | min = request.data.get('min', 0) 100 | if not dev_id or not name: 101 | return JsonResponse(error_msg) 102 | if qos not in [0, 1, 2, '0', '1', '2'] or \ 103 | min not in [0, 1, 2, 3, 4, 5, 6, 7, '0', '1', '2', '3', '4', '5', '6','7']: 104 | return JsonResponse(error_msg) 105 | res = database.add_stream(request.user, dev_id, name, unit, unit_symbol, max, min, qos) 106 | return JsonResponse(res) 107 | 108 | def put(self, request): 109 | """修改数据流信息""" 110 | device_id = request.data.get('dev_id', None) 111 | name = request.data.get('name', None) 112 | unit = request.data.get('unit', None) 113 | unit_symbol = request.data.get('unit_symbol', None) 114 | max = request.data.get('max', None) 115 | min = request.data.get('min', None) 116 | qos = request.data.get('qos', None) 117 | if not device_id or not name: 118 | return JsonResponse(error_msg) 119 | if qos not in [0, 1, 2, '0', '1', '2'] or \ 120 | min not in [0, 1, 2, 3, 4, 5, 6, 7, '0', '1', '2', '3', '4', '5', '6','7']: 121 | return JsonResponse(error_msg) 122 | res = database.update_stream(request.user, device_id, name, unit, unit_symbol, max, min, qos) 123 | return JsonResponse(res) 124 | 125 | def delete(self, request): 126 | dev_id = request.data.get('dev_id', None) 127 | name = request.data.get('name', None) 128 | res = database.del_stream(request.user, dev_id, name) 129 | return JsonResponse(res) 130 | 131 | 132 | class Console(APIView): 133 | parser_classes = [JSONParser, FormParser] 134 | 135 | def post(self, request): 136 | topic = request.data.get('topic') 137 | # topic 为必要参数必须要有 138 | if not topic: 139 | return JsonResponse(error_msg) 140 | # 此项参数允许为空 141 | payload = request.data.get('payload', None) 142 | qos = request.data.get('qos') 143 | # qos 参数只能为列表中的几个,否则无效 144 | if qos not in ['0', '1', '2', 1, 2, 0]: 145 | return JsonResponse(error_msg) 146 | data = {'topic': topic, 'payload': payload, 'qos': int(qos)} 147 | res = send(data) 148 | return JsonResponse(res) 149 | 150 | 151 | class Trigger(APIView): 152 | parser_classes = [JSONParser, FormParser] 153 | 154 | def get(self, request): 155 | res = database.get_trigger(request.user) 156 | print(res) 157 | return JsonResponse(res) 158 | -------------------------------------------------------------------------------- /IOTPlatform/weixin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/weixin/__init__.py -------------------------------------------------------------------------------- /IOTPlatform/weixin/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/weixin/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/weixin/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/weixin/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/weixin/__pycache__/admin.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/weixin/__pycache__/admin.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/weixin/__pycache__/admin.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/weixin/__pycache__/admin.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/weixin/__pycache__/models.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/weixin/__pycache__/models.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/weixin/__pycache__/models.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/weixin/__pycache__/models.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/weixin/__pycache__/urls.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/weixin/__pycache__/urls.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/weixin/__pycache__/urls.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/weixin/__pycache__/urls.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/weixin/__pycache__/views.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/weixin/__pycache__/views.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/weixin/__pycache__/views.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/weixin/__pycache__/views.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/weixin/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /IOTPlatform/weixin/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class WeixinConfig(AppConfig): 5 | name = 'weixin' 6 | -------------------------------------------------------------------------------- /IOTPlatform/weixin/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/weixin/migrations/__init__.py -------------------------------------------------------------------------------- /IOTPlatform/weixin/migrations/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/weixin/migrations/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /IOTPlatform/weixin/migrations/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/IOTPlatform/weixin/migrations/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /IOTPlatform/weixin/models.py: -------------------------------------------------------------------------------- 1 | # This is an auto-generated Django model module. 2 | # You'll have to do the following manually to clean this up: 3 | # * Rearrange models' order 4 | # * Make sure each model has one field with primary_key=True 5 | # * Make sure each ForeignKey has `on_delete` set to the desired behavior. 6 | # * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table 7 | # Feel free to rename the models, but don't rename db_table values or field names. 8 | from django.db import models 9 | 10 | -------------------------------------------------------------------------------- /IOTPlatform/weixin/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /IOTPlatform/weixin/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from weixin import views 3 | urlpatterns = [ 4 | url(r'^login$', views.Login.as_view()), # 登陆验证 5 | ] -------------------------------------------------------------------------------- /IOTPlatform/weixin/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, HttpResponse 2 | from rest_framework.views import APIView 3 | 4 | 5 | # Create your views here. 6 | 7 | class Login(APIView): 8 | def get(self, requset): 9 | """ 10 | 11 | :param requset: 12 | :return: 13 | """ 14 | return HttpResponse('111k') 15 | -------------------------------------------------------------------------------- /MQTT/.idea/MQTT.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /MQTT/.idea/dictionaries/fml.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pymysql 5 | 6 | 7 | -------------------------------------------------------------------------------- /MQTT/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | -------------------------------------------------------------------------------- /MQTT/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /MQTT/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MQTT/__pycache__/database.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/MQTT/__pycache__/database.cpython-37.pyc -------------------------------------------------------------------------------- /MQTT/__pycache__/models.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanmlei/IOTPlatform/b137568d27d0a41fa25e66f51e4ffb83d74f36f6/MQTT/__pycache__/models.cpython-37.pyc -------------------------------------------------------------------------------- /MQTT/database.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine 2 | from sqlalchemy.orm import sessionmaker 3 | from models import * # 导入生成的model 4 | 5 | from datetime import datetime 6 | 7 | 8 | class DB(object): 9 | def __init__(self, name, password, host, port, dbname): 10 | # 初始化的过程中直接连接数据库 11 | engine = create_engine('mysql+pymysql://' + name + ':' + password + '@' + host + ':' + port + '/' + dbname) 12 | self.Session = sessionmaker(bind=engine) 13 | self.session = self.Session() 14 | 15 | def reconnect(self): 16 | """"断开重连""" 17 | self.session = self.Session() 18 | 19 | def update_dev_stus(self, device_id, status): 20 | """ 21 | 更改设备状态 22 | :param device_id: 设备ID 23 | :param status: 状态 24 | :return: True 25 | """ 26 | try: 27 | self.session.query(Device).filter(Device.device_id == device_id).update( 28 | {Device.dev_status: status}) 29 | self.session.flush() 30 | self.session.commit() 31 | return True 32 | except Exception as e: 33 | self.reconnect() 34 | 35 | def write_data(self, device_id, dev_stream, data): 36 | """ 37 | 写入数据 38 | :param device_id: 设备ID 39 | :param dev_stream: 数据流名称 40 | :param data: 数据 41 | :return: True 42 | """ 43 | try: 44 | new_data = Data(data=data,date=datetime.now()) 45 | device_obj = self.session.query(Device).filter(Device.device_id==device_id).first() 46 | if device_obj: 47 | for i in device_obj.stream: 48 | if i.name == dev_stream: 49 | i.data.append(new_data) 50 | self.session.flush() 51 | self.session.commit() 52 | return True 53 | except Exception as e: 54 | self.reconnect() 55 | 56 | 57 | def get_subscribe(self): 58 | """ 59 | 获取需要订阅的主题名 60 | :return: list 61 | """ 62 | try: 63 | subscribe_list = [] 64 | devive_objs = self.session.query(Device).all() 65 | for i in devive_objs: 66 | for j in i.stream: 67 | subscribe_list.append([str(i.device_id) + '/' + j.name, j.qos]) 68 | return subscribe_list 69 | except Exception as e: 70 | self.reconnect() 71 | 72 | 73 | 74 | 75 | # db = DB('root', 'fml950826cs','123.207.87.193','3306','IOTPlatform') 76 | 77 | # db.update_dev_stus(123456,1) 78 | # db.write_data('123456', 'temp1', '213213123') 79 | # db.update_dev_stus('123456',1) 80 | 81 | # print(db.get_subscribe()) 82 | -------------------------------------------------------------------------------- /MQTT/models.py: -------------------------------------------------------------------------------- 1 | # sqlacodegen mysql+pymysql://root:fml950826cs@123.207.87.193:3306/IOTPlatform > models.py 2 | # 3 | 4 | # coding: utf-8 5 | from sqlalchemy import Column, ForeignKey, Index, String,Table,Integer 6 | from sqlalchemy.dialects.mysql import DATETIME, INTEGER, LONGTEXT, SMALLINT, TINYINT, VARCHAR 7 | from sqlalchemy.orm import relationship 8 | from sqlalchemy.ext.declarative import declarative_base 9 | 10 | Base = declarative_base() 11 | metadata = Base.metadata 12 | 13 | stream_data = Table( 14 | 'website_datastream_data', Base.metadata, 15 | Column('datastream_id', Integer, ForeignKey('website_datastream.id')), 16 | Column('data_id', Integer, ForeignKey('website_data.id')) 17 | ) 18 | 19 | device_stream = Table( 20 | 'website_device_dev_stream', Base.metadata, 21 | Column('device_id', Integer, ForeignKey('website_device.id')), 22 | Column('datastream_id', Integer, ForeignKey('website_datastream.id')) 23 | ) 24 | 25 | class Data(Base): 26 | __tablename__ = 'website_data' 27 | id = Column(INTEGER(11), primary_key=True) 28 | data = Column(String(10), nullable=False) 29 | date = Column(DATETIME(fsp=6), nullable=False) 30 | 31 | class Stream(Base): 32 | __tablename__ = 'website_datastream' 33 | 34 | id = Column(INTEGER(11), primary_key=True) 35 | name = Column(VARCHAR(32), nullable=False) 36 | min = Column(INTEGER(11)) 37 | max = Column(INTEGER(11)) 38 | qos = Column(INTEGER(11), nullable=False) 39 | unit = Column(VARCHAR(32)) 40 | unit_symbol = Column(VARCHAR(32)) 41 | trigger = Column(TINYINT(1), nullable=False) 42 | data = relationship('Data', secondary=stream_data) 43 | 44 | class Device(Base): 45 | __tablename__ = 'website_device' 46 | 47 | id = Column(INTEGER(11), primary_key=True) 48 | device_id = Column(INTEGER(11), nullable=False, unique=True) 49 | device_key = Column(String(32)) 50 | device_name = Column(VARCHAR(32), nullable=False) 51 | dev_status = Column(TINYINT(1), nullable=False) 52 | apiKey = Column(String(32)) 53 | date = Column(DATETIME(fsp=6), nullable=False) 54 | dev_introduce = Column(LONGTEXT) 55 | tag = Column(VARCHAR(32)) 56 | stream = relationship('Stream', secondary=device_stream) 57 | 58 | # class WebsiteData(Base): 59 | # __tablename__ = 'website_data' 60 | # 61 | # id = Column(INTEGER(11), primary_key=True) 62 | # data = Column(String(10), nullable=False) 63 | # date = Column(DATETIME(fsp=6), nullable=False) 64 | 65 | 66 | # class WebsiteDatastream(Base): 67 | # __tablename__ = 'website_datastream' 68 | # 69 | # id = Column(INTEGER(11), primary_key=True) 70 | # name = Column(VARCHAR(32), nullable=False) 71 | # min = Column(INTEGER(11)) 72 | # max = Column(INTEGER(11)) 73 | # qos = Column(INTEGER(11), nullable=False) 74 | # unit = Column(VARCHAR(32)) 75 | # unit_symbol = Column(VARCHAR(32)) 76 | # trigger = Column(TINYINT(1), nullable=False) 77 | 78 | 79 | # class WebsiteDevice(Base): 80 | # __tablename__ = 'website_device' 81 | # 82 | # id = Column(INTEGER(11), primary_key=True) 83 | # device_id = Column(INTEGER(11), nullable=False, unique=True) 84 | # device_key = Column(String(32)) 85 | # device_name = Column(VARCHAR(32), nullable=False) 86 | # dev_status = Column(TINYINT(1), nullable=False) 87 | # apiKey = Column(String(32)) 88 | # date = Column(DATETIME(fsp=6), nullable=False) 89 | # dev_introduce = Column(LONGTEXT) 90 | # tag = Column(VARCHAR(32)) 91 | 92 | 93 | # class WebsiteUserinfo(Base): 94 | # __tablename__ = 'website_userinfo' 95 | # 96 | # id = Column(INTEGER(11), primary_key=True) 97 | # username = Column(VARCHAR(32), nullable=False, unique=True) 98 | # user_id = Column(INTEGER(11), nullable=False, unique=True) 99 | # password = Column(VARCHAR(64), nullable=False) 100 | # weixin_id = Column(VARCHAR(64), index=True) 101 | # sex = Column(VARCHAR(16)) 102 | # birthday = Column(VARCHAR(64)) 103 | # tel = Column(VARCHAR(64)) 104 | # email = Column(VARCHAR(64)) 105 | # address = Column(VARCHAR(64)) 106 | # introduction = Column(VARCHAR(255)) 107 | # token = Column(VARCHAR(255)) 108 | 109 | 110 | # class WebsiteDatastreamData(Base): 111 | # __tablename__ = 'website_datastream_data' 112 | # __table_args__ = ( 113 | # Index('website_datastream_data_datastream_id_data_id_3b3216eb_uniq', 'datastream_id', 'data_id', unique=True), 114 | # ) 115 | # 116 | # id = Column(INTEGER(11), primary_key=True) 117 | # datastream_id = Column(ForeignKey('website_datastream.id'), nullable=False) 118 | # data_id = Column(ForeignKey('website_data.id'), nullable=False, index=True) 119 | # 120 | # data = relationship('WebsiteData') 121 | # datastream = relationship('WebsiteDatastream') 122 | 123 | 124 | # class WebsiteDeviceDevStream(Base): 125 | # __tablename__ = 'website_device_dev_stream' 126 | # __table_args__ = ( 127 | # Index('website_device_dev_stream_device_id_datastream_id_6d32e5c0_uniq', 'device_id', 'datastream_id', unique=True), 128 | # ) 129 | # 130 | # id = Column(INTEGER(11), primary_key=True) 131 | # device_id = Column(ForeignKey('website_device.id'), nullable=False) 132 | # datastream_id = Column(ForeignKey('website_datastream.id'), nullable=False, index=True) 133 | # 134 | # datastream = relationship('WebsiteDatastream') 135 | # device = relationship('WebsiteDevice') 136 | 137 | 138 | # class WebsiteUserinfoDevice(Base): 139 | # __tablename__ = 'website_userinfo_device' 140 | # __table_args__ = ( 141 | # Index('website_userinfo_device_userinfo_id_device_id_e34001e8_uniq', 'userinfo_id', 'device_id', unique=True), 142 | # ) 143 | # 144 | # id = Column(INTEGER(11), primary_key=True) 145 | # userinfo_id = Column(ForeignKey('website_userinfo.id'), nullable=False) 146 | # device_id = Column(ForeignKey('website_device.id'), nullable=False, index=True) 147 | # 148 | # device = relationship('WebsiteDevice') 149 | # userinfo = relationship('WebsiteUserinfo') 150 | -------------------------------------------------------------------------------- /MQTT/mqtt_client.py: -------------------------------------------------------------------------------- 1 | import paho.mqtt.client as client 2 | from database import DB 3 | import threading 4 | 5 | 6 | class MqClient(object): 7 | def __init__(self, client_id, username, password): 8 | self.client = client.Client(client_id=client_id, 9 | clean_session=False) # 初始化,clean_session为false的时候EMQ会保存订阅状态,可以不再次订阅 10 | self.client.username_pw_set(username, password) # 设置连接用户名 11 | self.client.on_connect = self.on_connect 12 | self.client.on_message = self.on_message 13 | self._client_status = False # 连接状态 14 | self._cloop = None 15 | self._connect() 16 | self.db = DB('root', 'fml950826cs', '123.207.87.193', '3306', 'IOTPlatform') 17 | 18 | def _connect(self, host="123.207.87.193", port=1883, keepalive=60): 19 | """连接服务器""" 20 | self.client.connect_async(host, port, keepalive) 21 | # self.client.loop_forever() 22 | # 开启线程执行 23 | self._cloop = threading.Thread(target=self.client.loop_start()) 24 | self._cloop.start() 25 | 26 | def on_connect(self, client, userdata, flags, rc): 27 | """连接成功的回调函数""" 28 | # 订阅上下线消息 29 | self.client.subscribe("$SYS/brokers/emq@127.0.0.1/clients/#") 30 | 31 | # 读取数据库中所有的已经注册过的topic并且订阅 32 | sub_list = self.db.get_subscribe() 33 | # 防止没有获取到订阅列表 34 | while not sub_list: 35 | sub_list = self.db.get_subscribe() 36 | for i in sub_list: 37 | print(i[0],i[1]) 38 | self.client.subscribe(i[0], i[1]) 39 | # 修改客户端状态 40 | if rc == 0: 41 | self._client_status = True 42 | 43 | def on_message(self, client, userdata, msg): 44 | topic = msg.topic.split('/') 45 | client_id = topic[0] 46 | # 系统主题 47 | if client_id == '$SYS': 48 | status = 1 if topic[-1] == 'connected' else 0 49 | device_id = topic[-2] 50 | while not self.db.update_dev_stus(device_id, status): 51 | pass 52 | # 设备消息 53 | else: 54 | stream = topic[1] 55 | data = msg.payload.decode() 56 | while not self.db.write_data(client_id, stream, data): 57 | pass 58 | 59 | 60 | 61 | 62 | 63 | c = MqClient('System', '123', '123') 64 | while True: 65 | pass -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ### 项目简介 2 | 项目基于MQTT协议,采用EMQ消息服务器实现MQTT通信功能。设备接入需完成auth认证,如果需要对消息进行持久化处理还需要在后台注册你的设备和设备流信息,除此之外还可用于组件自己的网络,自定义主题收发消息即可。在后台可直接对已注册的设备发送指令,默认发送主题为$system/clientID,也可自定义发送主题(系统主题除外)。后台提供对设备、数据流、触发器的管理、查看历史上传数据、下发指令等功能。 3 | ### 开发环境 4 | * 后端: 5 | * Django 2.1.2 6 | * django-cors-headers 2.4.0 7 | * djangorestframework 3.9.0 8 | * requests 2.20.0 9 | * PyMySQL 0.9.2 10 | * 前端: 11 | * axios 0.18.0 12 | * echarts 4.2.0 13 | * element-ui 2.4.9 14 | * vue 2.5.2 15 | * vue-router 3.0.1 16 | * vuex 3.0.1 17 | * 硬件: 18 | * pubsubclient 19 | * 消息持久化服务: 20 | * paho-mqtt 1.4.0 21 | * SQLAlchemy 1.2.14 22 | * 数据库: 23 | * MySQL 5.7.0 24 | * EMQ: 25 | * emqx 2.3.0 26 | 27 | ### 项目整体架构 28 | 由EMQ服务器管理设备接入认证、消息分发,项目基本没有对EMQ服务做出修改,主要是对EMQ的权限认证、访问控制规则做了相应的变化,具体的修改方法请查看博客连载https://blog.csdn.net/FanMLei
29 | 30 | 消息持久化服务使用paho-mqtt实现mqtt通信对注册数据流进行监听,并将收到的消息写入数据库中。服务独立于后端项目独运行方便日后的扩展,每隔一分钟会刷新订阅列表,防止新注册的数据流无法接收消息。
31 | 32 | 后端提供设备信息、数据流信息、触发器信息、历史数据的管理接口以及网站的常规接口,接口使用rest Framework框架实现,同时通过API接口获取EMQ服务的状态信息和下发指令
33 | 34 | 前端负责数据展示,界面使用elementUI,图表使用echarts进行渲染,通过axios获取数据内容
35 | 36 | ### 项目结构简介 37 | APPFont: 前端项目
38 | Hardware: 硬件连接测试代码
39 | IOTPlatform: 后端项目
40 | MQTT: 消息持久化服务
41 | 42 | ### 更新计划 43 | >>>手机端页面适配(网页或者微信小程序)
44 | >>>完善触发器通知
45 | >>>完善接入指南、增加更多的硬件连接示例
46 | >>>API接口
47 | 48 | ### 联系方式 49 | Email : 15638515832@163.com
50 | QQ: 1193589986
51 | 博客: https://blog.csdn.net/FanMLei 52 | --------------------------------------------------------------------------------