├── .env ├── .prettierrc ├── package.json ├── .gitignore ├── index.js ├── src ├── log.js ├── common.js ├── main.js └── api.js ├── LICENSE ├── README.md ├── .github └── workflows │ └── sport-editor-task.yml └── data.json /.env: -------------------------------------------------------------------------------- 1 | STED_SIZE_RANGE=8000-15000 -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "printWidth": 100, 4 | "semi": true, 5 | "singleQuote": false, 6 | "bracketSpacing": true 7 | } 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sport-editor", 3 | "version": "1.0.0", 4 | "description": "", 5 | "author": "devifish", 6 | "private": true, 7 | "main": "index.js", 8 | "type": "module", 9 | "scripts": { 10 | "start": "node index.js" 11 | }, 12 | "dependencies": { 13 | "axios": "^0.21.1", 14 | "dayjs": "^1.10.4" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | 25 | # npm 26 | package-lock.json 27 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { run } from "./src/main.js"; 2 | 3 | const DEFAULT_STEP_SIZE = "5000-15000"; 4 | 5 | // 获取环境变量 6 | const config = { 7 | username: process.env.XIAOMI_AMAZFIT_USERNAME, 8 | password: process.env.XIAOMI_AMAZFIT_PASSWORD, 9 | user_id: process.env.XIAOMI_AMAZFIT_USER_ID, 10 | app_token: process.env.XIAOMI_AMAZFIT_APP_TOKEN, 11 | step_size: process.env.STED_SIZE_RANGE ?? DEFAULT_STEP_SIZE, 12 | }; 13 | 14 | await run(config); 15 | -------------------------------------------------------------------------------- /src/log.js: -------------------------------------------------------------------------------- 1 | import dayjs from "dayjs"; 2 | 3 | const LogType = { 4 | INFO: "info", 5 | WARN: "warn", 6 | ERROR: "error" 7 | }; 8 | 9 | function log(msg, type = LogType.INFO) { 10 | console.log(`[${dayjs().format("YYYY-MM-DD HH:mm:ss.SSS")}] [${type}] ${msg}`); 11 | } 12 | 13 | export const info = (msg) => log(msg, LogType.INFO); 14 | export const warn = (msg) => log(msg, LogType.WARN); 15 | export const error = (msg) => log(msg, LogType.ERROR); 16 | -------------------------------------------------------------------------------- /src/common.js: -------------------------------------------------------------------------------- 1 | export function toUrlEncode(obj) { 2 | const data = new URLSearchParams(); 3 | 4 | const keys = Object.keys(obj); 5 | keys.forEach((key) => data.append(key, obj[key])); 6 | return data; 7 | } 8 | 9 | export function isEmpty(obj) { 10 | if (typeof obj === "string") { 11 | return obj.length === 0; 12 | } else if (obj instanceof Array) { 13 | return obj.length === 0; 14 | } else if (obj instanceof Set) { 15 | return obj.size === 0; 16 | } else if (obj === null) { 17 | return true; 18 | } else if (obj === undefined) { 19 | return true; 20 | } else { 21 | return false; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { loginByPassword, getAccessToken, pushBandData } from "./api.js"; 2 | import { isEmpty } from "./common.js"; 3 | import * as log from "./log.js"; 4 | 5 | export async function run(config) { 6 | if (isEmpty(config.app_token) || isEmpty(config.user_id)) { 7 | log.warn("未获取到APP_TOKEN或USER_ID 将使用账号密码方式运行"); 8 | const code = await loginByPassword(config.username, config.password); 9 | const { app_token, user_id } = await getAccessToken(code); 10 | 11 | config.app_token = app_token; 12 | config.user_id = user_id; 13 | } 14 | 15 | const step = getRamdomStep(config.step_size); 16 | await pushBandData(step, config.user_id, config.app_token); 17 | } 18 | 19 | function getRamdomStep(step_size = DEFAULT_STEP_SIZE) { 20 | if (!step_size.includes("-")) throw new Error("步数范围格式异常"); 21 | 22 | const temp = step_size.split("-"); 23 | if (temp.length !== 2) return getRamdomStep(); 24 | 25 | const min = new Number(temp[0]); 26 | const max = new Number(temp[1]); 27 | const step = parseInt(min + Math.random() * (max - min)); 28 | log.info(`在 [${min} 至 ${max}] 范围内随机步数 step: ${step}`); 29 | return step; 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Devifish 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 运动步数修改工具 2 | 3 | [![sport-editor-task](https://github.com/Devifish/sport-editor/actions/workflows/sport-editor-task.yml/badge.svg)](https://github.com/Devifish/sport-editor/actions/workflows/sport-editor-task.yml) 4 | [![star](https://img.shields.io/github/stars/Devifish/sport-editor.svg?logo=github)](https://github.com/Devifish/sport-editor) 5 | [![license](https://img.shields.io/github/license/Devifish/sport-editor)](https://github.com/Devifish/sport-editor) 6 | 7 | > 通过华米运动的 API 提交运动步数 😒
8 | > 可实现同步运动步数至热门平台,如微信、支付宝等 9 | 10 | ## 实现功能 11 | 12 | - [x] 每日自动更新步数 13 | - [x] 指定随机运动步数范围 14 | - [x] Github Action 运行 15 | 16 | ## 配置参数 17 | 18 | | 环境变量 | 说明 | 值 | 19 | | ------------------------ | ------------------- | ------------------- | 20 | | XIAOMI_AMAZFIT_USERNAME | 用户名 | 159000000 | 21 | | XIAOMI_AMAZFIT_PASSWORD | 密码 | xxxxxx | 22 | | XIAOMI_AMAZFIT_USER_ID | 用户 ID (可选) | 123456 | 23 | | XIAOMI_AMAZFIT_APP_TOKEN | APP Token (可选) | xxxxxxxx | 24 | | STED_SIZE_RANGE | 运动步数范围 (可选) | 5000-15000 (默认值) | 25 | 26 | - 推荐使用 Node.js 12 及以上的运行/构建当前项目 27 | - 运动步数范围使用 - 分隔上下限 28 | - 可选账号/密码、UserID/AppToken 两种方式运行 29 | 30 | ## Github Actions 部署 31 | 32 | - fork 本项目 33 | - 将环境变量参数填到 Setting -> Secrets (如果使用 Token+ID 方式可不填用户名及密码) 34 | - 开启 actions (默认`actions`处于禁止状态) 35 | - 执行 sport-editor-task workflow 36 | 37 | ## 命令行运行 38 | 39 | ``` 40 | git clone https://github.com/Devifish/sport-editor.git 41 | cd sport-editor 42 | npm install 43 | npm run start 44 | ``` 45 | -------------------------------------------------------------------------------- /.github/workflows/sport-editor-task.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: sport-editor-task 5 | 6 | on: 7 | workflow_dispatch: # 手动触发 8 | schedule: # 计划任务触发 (UTC时区 H+8) 9 | - cron: "0 4 * * *" 10 | 11 | jobs: 12 | run-sport-editor: 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | matrix: 17 | node-version: [14.x] 18 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | 23 | # 设置服务器时区为东八区 24 | - name: Set time zone 25 | run: sudo timedatectl set-timezone 'Asia/Shanghai' 26 | 27 | # Node.js 环境 28 | - name: Use Node.js ${{ matrix.node-version }} 29 | uses: actions/setup-node@v1 30 | with: 31 | node-version: ${{ matrix.node-version }} 32 | 33 | # 安装Npm依赖 34 | - name: Install dependencies 35 | run: npm install 36 | 37 | # 运行 38 | - name: Run APP 39 | env: 40 | XIAOMI_AMAZFIT_USERNAME: ${{secrets.XIAOMI_AMAZFIT_USERNAME}} 41 | XIAOMI_AMAZFIT_PASSWORD: ${{secrets.XIAOMI_AMAZFIT_PASSWORD}} 42 | XIAOMI_AMAZFIT_USER_ID: ${{secrets.XIAOMI_AMAZFIT_USER_ID}} 43 | XIAOMI_AMAZFIT_APP_TOKEN: ${{secrets.XIAOMI_AMAZFIT_APP_TOKEN}} 44 | STED_SIZE_RANGE: ${{secrets.STED_SIZE_RANGE}} 45 | 46 | run: npm run start 47 | -------------------------------------------------------------------------------- /src/api.js: -------------------------------------------------------------------------------- 1 | import Axios from "axios"; 2 | import dayjs from "dayjs"; 3 | import fs from "fs/promises"; 4 | import { toUrlEncode } from "./common.js"; 5 | import * as log from "./log.js"; 6 | 7 | // 公共头 8 | const COMMON_HEADERS = { 9 | "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", 10 | "User-Agent": "MiFit/4.6.0 (iPhone; iOS 14.0.1; Scale/2.00)", 11 | }; 12 | 13 | // 初始化请求工具 14 | const axios = Axios.create({ 15 | timeout: 30000, 16 | headers: { 17 | ...COMMON_HEADERS, 18 | }, 19 | }); 20 | 21 | export async function loginByPassword(username, password) { 22 | const redirect_uri = new URL( 23 | "https://s3-us-west-2.amazonaws.com/hm-registration/successsignin.html" 24 | ); 25 | const data = toUrlEncode({ 26 | client_id: "HuaMi", 27 | password: password, 28 | redirect_uri: redirect_uri.toString(), 29 | token: "access", 30 | }); 31 | 32 | try { 33 | const res = await axios.post( 34 | `https://api-user.huami.com/registrations/+86${username}/tokens`, 35 | data 36 | ); 37 | log.info("登录成功, 开始获取登录授权码"); 38 | 39 | // 获取Code 40 | const path = new URL(res.request.path, redirect_uri); 41 | const params = path.searchParams; 42 | if (params.has("access")) { 43 | const code = params.get("access"); 44 | log.info(`获取登录授权码成功 code: ${code}`); 45 | return code; 46 | } 47 | throw new Error("获取登录授权码失败"); 48 | } catch (e) { 49 | log.error("登录失败, 请检查账号密码"); 50 | throw e; 51 | } 52 | } 53 | 54 | /** 55 | * 获取 AccessToken 56 | * { 57 | * token_info: { 58 | * login_token: 'NQVBQFJyQktGHlp6QkpbRl5LRl5qek4uXAQEBAAAAAFqkxITXYP0BwB38qOixn1nCAXBsdjhuDu3uXhorgfSgwXaEu3PX3Su4D3ORnnUI9po6jCIrFI_RyMl357xiiyvKhdGS8hotvmZFv50E3sQ6KZ-W1v0hOazY-fBTnB-R4_qw8G7TDE3EnqxDJ7kbJ46lQgAY67L8shTBpThIKz-fZu2TDeoYtwOj1-_EfrwpVrob2n3u5C_YXAd3q4ZQtZ4', 59 | * app_token: 'NQVBQFJyQktGHlp6QkpbRl5LRl5qek4uXAQABAAAAAA4FfIlo7RFwnLXNgwDjJ-14ZEU8FKumnnEUS-__PJ0WtA2fV46czDEzWPGK2QlliMzVZVUd0R-y56Da7esYbNWuZTc2agnVq6wJ_Oz8YISxxS9mA-EepMgb9Bnrd1T5-yHiNMjwvOT3fs1f-zOts2QFjDWMoXqvQtnEe1ekKfvc0Cg4vdtuLk1oklwDymtQIQ', 60 | * user_id: '1102541833', 61 | * ttl: 31536000, 62 | * app_ttl: 43200 63 | * }, 64 | * regist_info: { 65 | * is_new_user: 0, 66 | * regist_date: 1615864097080, 67 | * region: '1', 68 | * country_code: 'CN' 69 | * }, 70 | * thirdparty_info: { 71 | * nickname: '', 72 | * icon: '', 73 | * third_id: 'NLD3d7BO_r1pzu558H9_F6wAAAXg5AWT1', 74 | * email: '+8615070574275' 75 | * }, 76 | * result: 'ok', 77 | * domain: { 'id-dns': 'https://account-cn2.huami.com' } 78 | * } 79 | * 80 | * @param {code} code 81 | * @returns 82 | */ 83 | export async function getAccessToken(code) { 84 | const data = toUrlEncode({ 85 | app_name: "com.xiaomi.hm.health", 86 | app_version: "4.6.0", 87 | code: code, 88 | country_code: "CN", 89 | device_id: "2C8B4939-0CCD-4E94-8CBA-CB8EA6E613A1", 90 | device_model: "phone", 91 | grant_type: "access_token", 92 | third_name: "huami_phone", 93 | }); 94 | 95 | try { 96 | const res = await axios.post("https://account.huami.com/v2/client/login", data); 97 | 98 | const token_info = res.data.token_info; 99 | log.info(`获取AccessToken成功 token: ${token_info.login_token}`); 100 | return token_info; 101 | } catch (e) { 102 | log.error("获取AccessToken失败"); 103 | throw e; 104 | } 105 | } 106 | 107 | export async function pushBandData(step, user_id, app_token) { 108 | const data = toUrlEncode({ 109 | userid: user_id, 110 | last_sync_data_time: 1597306380, 111 | device_type: 0, 112 | last_deviceid: "DA932FFFFE8816E7", 113 | data_json: await buildDataJson(step), 114 | }); 115 | 116 | try { 117 | const res = await axios.post( 118 | `https://api-mifit-cn.huami.com/v1/data/band_data.json?&t=${Date.now()}`, 119 | data, 120 | { 121 | headers: { 122 | apptoken: app_token, 123 | }, 124 | } 125 | ); 126 | 127 | log.info(`上传步数成功 step:${step}`); 128 | } catch (e) { 129 | log.error("上传步数失败"); 130 | throw e; 131 | } 132 | } 133 | 134 | async function buildDataJson(step) { 135 | const time = dayjs().format("YYYY-MM-DD"); 136 | const find_date = /.*?date":"(.*?)","data.*?/; 137 | const find_step = /.*?ttl\\":(.*?),\\"dis.*?/; 138 | 139 | let data_json = await fs.readFile("./data.json", "utf-8"); 140 | data_json = data_json.replace(find_date.exec(data_json)[1], time); 141 | data_json = data_json.replace(find_step.exec(data_json)[1], step); 142 | return data_json; 143 | } 144 | -------------------------------------------------------------------------------- /data.json: -------------------------------------------------------------------------------- 1 | [{"data_hr":"\/\/\/\/\/\/9L\/\/\/\/\/\/\/\/\/\/\/\/Vv\/\/\/\/\/\/\/\/\/\/\/0v\/\/\/\/\/\/\/\/\/\/\/9e\/\/\/\/\/0n\/a\/\/\/S\/\/\/\/\/\/\/\/\/\/\/\/0b\/\/\/\/\/\/\/\/\/\/1FK\/\/\/\/\/\/\/\/\/\/\/\/R\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/9PTFFpaf9L\/\/\/\/\/\/\/\/\/\/\/\/R\/\/\/\/\/\/\/\/\/\/\/\/0j\/\/\/\/\/\/\/\/\/\/\/9K\/\/\/\/\/\/\/\/\/\/\/\/Ov\/\/\/\/\/\/\/\/\/\/\/zf\/\/\/86\/zr\/Ov88\/zf\/Pf\/\/\/0v\/S\/8\/\/\/\/\/\/\/\/\/\/\/\/\/Sf\/\/\/\/\/\/\/\/\/\/\/z3\/\/\/\/\/\/0r\/Ov\/\/\/\/\/\/S\/9L\/zb\/Sf9K\/0v\/Rf9H\/zj\/Sf9K\/0\/\/N\/\/\/\/0D\/Sf83\/zr\/Pf9M\/0v\/Ov9e\/\/\/\/\/\/\/\/\/\/\/\/S\/\/\/\/\/\/\/\/\/\/\/\/zv\/\/z7\/O\/83\/zv\/N\/83\/zr\/N\/86\/z\/\/Nv83\/zn\/Xv84\/zr\/PP84\/zj\/N\/9e\/zr\/N\/89\/03\/P\/89\/z3\/Q\/9N\/0v\/Tv9C\/0H\/Of9D\/zz\/Of88\/z\/\/PP9A\/zr\/N\/86\/zz\/Nv87\/0D\/Ov84\/0v\/O\/84\/zf\/MP83\/zH\/Nv83\/zf\/N\/84\/zf\/Of82\/zf\/OP83\/zb\/Mv81\/zX\/R\/9L\/0v\/O\/9I\/0T\/S\/9A\/zn\/Pf89\/zn\/Nf9K\/07\/N\/83\/zn\/Nv83\/zv\/O\/9A\/0H\/Of8\/\/zj\/PP83\/zj\/S\/87\/zj\/Nv84\/zf\/Of83\/zf\/Of83\/zb\/Nv9L\/zj\/Nv82\/zb\/N\/85\/zf\/N\/9J\/zf\/Nv83\/zj\/Nv84\/0r\/Sv83\/zf\/MP\/\/\/zb\/Mv82\/zb\/Of85\/z7\/Nv8\/\/0r\/S\/85\/0H\/QP9B\/0D\/Nf89\/zj\/Ov83\/zv\/Nv8\/\/0f\/Sv9O\/0ZeXv\/\/\/\/\/\/\/\/\/\/\/1X\/\/\/\/\/\/\/\/\/\/\/9B\/\/\/\/\/\/\/\/\/\/\/\/TP\/\/\/1b\/\/\/\/\/\/0\/\/\/\/\/\/\/\/\/\/\/\/9N\/\/\/\/\/\/\/\/\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+\/v7+","date":"2020-08-14","data":[{"start":0,"stop":1439,"value":"UA8AUBQAUAwAUBoAUAEAYCcAUBkAUB4AUBgAUCAAUAEAUBkAUAwAYAsAYB8AYB0AYBgAYCoAYBgAYB4AUCcAUBsAUB8AUBwAUBIAYBkAYB8AUBoAUBMAUCEAUCIAYBYAUBwAUCAAUBgAUCAAUBcAYBsAYCUAATIPYD0KECQAYDMAYB0AYAsAYCAAYDwAYCIAYB0AYBcAYCQAYB0AYBAAYCMAYAoAYCIAYCEAYCYAYBsAYBUAYAYAYCIAYCMAUB0AUCAAUBYAUCoAUBEAUC8AUB0AUBYAUDMAUDoAUBkAUC0AUBQAUBwAUA0AUBsAUAoAUCEAUBYAUAwAUB4AUAwAUCcAUCYAUCwKYDUAAUUlEC8IYEMAYEgAYDoAYBAAUAMAUBkAWgAAWgAAWgAAWgAAWgAAUAgAWgAAUBAAUAQAUA4AUA8AUAkAUAIAUAYAUAcAUAIAWgAAUAQAUAkAUAEAUBkAUCUAWgAAUAYAUBEAWgAAUBYAWgAAUAYAWgAAWgAAWgAAWgAAUBcAUAcAWgAAUBUAUAoAUAIAWgAAUAQAUAYAUCgAWgAAUAgAWgAAWgAAUAwAWwAAXCMAUBQAWwAAUAIAWgAAWgAAWgAAWgAAWgAAWgAAWgAAWgAAWREAWQIAUAMAWSEAUDoAUDIAUB8AUCEAUC4AXB4AUA4AWgAAUBIAUA8AUBAAUCUAUCIAUAMAUAEAUAsAUAMAUCwAUBYAWgAAWgAAWgAAWgAAWgAAWgAAUAYAWgAAWgAAWgAAUAYAWwAAWgAAUAYAXAQAUAMAUBsAUBcAUCAAWwAAWgAAWgAAWgAAWgAAUBgAUB4AWgAAUAcAUAwAWQIAWQkAUAEAUAIAWgAAUAoAWgAAUAYAUB0AWgAAWgAAUAkAWgAAWSwAUBIAWgAAUC4AWSYAWgAAUAYAUAoAUAkAUAIAUAcAWgAAUAEAUBEAUBgAUBcAWRYAUA0AWSgAUB4AUDQAUBoAXA4AUA8AUBwAUA8AUA4AUA4AWgAAUAIAUCMAWgAAUCwAUBgAUAYAUAAAUAAAUAAAUAAAUAAAUAAAUAAAUAAAUAAAWwAAUAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAeSEAeQ8AcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcBcAcAAAcAAAcCYOcBUAUAAAUAAAUAAAUAAAUAUAUAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcCgAeQAAcAAAcAAAcAAAcAAAcAAAcAYAcAAAcBgAeQAAcAAAcAAAegAAegAAcAAAcAcAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcCkAeQAAcAcAcAAAcAAAcAwAcAAAcAAAcAIAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcCIAeQAAcAAAcAAAcAAAcAAAcAAAeRwAeQAAWgAAUAAAUAAAUAAAUAAAUAAAcAAAcAAAcBoAeScAeQAAegAAcBkAeQAAUAAAUAAAUAAAUAAAUAAAUAAAcAAAcAAAcAAAcAAAcAAAcAAAegAAegAAcAAAcAAAcBgAeQAAcAAAcAAAcAAAcAAAcAAAcAkAegAAegAAcAcAcAAAcAcAcAAAcAAAcAAAcAAAcA8AeQAAcAAAcAAAeRQAcAwAUAAAUAAAUAAAUAAAUAAAUAAAcAAAcBEAcA0AcAAAWQsAUAAAUAAAUAAAUAAAUAAAcAAAcAoAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAYAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcBYAegAAcAAAcAAAegAAcAcAcAAAcAAAcAAAcAAAcAAAeRkAegAAegAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAEAcAAAcAAAcAAAcAUAcAQAcAAAcBIAeQAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcBsAcAAAcAAAcBcAeQAAUAAAUAAAUAAAUAAAUAAAUBQAcBYAUAAAUAAAUAoAWRYAWTQAWQAAUAAAUAAAUAAAcAAAcAAAcAAAcAAAcAAAcAMAcAAAcAQAcAAAcAAAcAAAcDMAeSIAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcAAAcBQAeQwAcAAAcAAAcAAAcAMAcAAAeSoAcA8AcDMAcAYAeQoAcAwAcFQAcEMAeVIAaTYAbBcNYAsAYBIAYAIAYAIAYBUAYCwAYBMAYDYAYCkAYDcAUCoAUCcAUAUAUBAAWgAAYBoAYBcAYCgAUAMAUAYAUBYAUA4AUBgAUAgAUAgAUAsAUAsAUA4AUAMAUAYAUAQAUBIAASsSUDAAUDAAUBAAYAYAUBAAUAUAUCAAUBoAUCAAUBAAUAoAYAIAUAQAUAgAUCcAUAsAUCIAUCUAUAoAUA4AUB8AUBkAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAAfgAA","tz":32,"did":"DA932FFFFE8816E7","src":24}],"summary":"{\"v\":6,\"slp\":{\"st\":1597349880,\"ed\":1597369860,\"dp\":39,\"lt\":294,\"wk\":0,\"usrSt\":-1440,\"usrEd\":-1440,\"wc\":0,\"is\":169,\"lb\":10,\"to\":23,\"dt\":0,\"rhr\":58,\"ss\":69,\"stage\":[{\"start\":1698,\"stop\":1711,\"mode\":4},{\"start\":1712,\"stop\":1728,\"mode\":5},{\"start\":1729,\"stop\":1818,\"mode\":4},{\"start\":1819,\"stop\":1832,\"mode\":5},{\"start\":1833,\"stop\":1920,\"mode\":4},{\"start\":1921,\"stop\":1928,\"mode\":5},{\"start\":1929,\"stop\":2030,\"mode\":4}]},\"stp\":{\"ttl\":125,\"dis\":82,\"cal\":5,\"wk\":7,\"rn\":0,\"runDist\":23,\"runCal\":3},\"goal\":8000,\"tz\":\"28800\",\"sn\":\"e716882f93da\"}","source":24,"type":0}] --------------------------------------------------------------------------------