├── README.md
├── custom_components
└── ha_baidu_map
│ ├── __init__.py
│ ├── api_config.py
│ ├── api_storage.py
│ ├── api_view.py
│ ├── config_flow.py
│ ├── const.py
│ ├── local
│ ├── ha-panel-baidu-map.js
│ └── travel.html
│ └── manifest.json
└── hacs.json
/README.md:
--------------------------------------------------------------------------------
1 | 本插件停止更新,推荐使用新版插件:https://github.com/shaonianzhentan/google_maps
2 |
3 | #### 关注我的微信订阅号,了解更多HomeAssistant相关知识
4 |
5 |
6 | # 百度地图
7 | 在HA里使用的百度地图,支持GPS定位轨迹显示
8 |
9 | # 使用方式
10 |
11 | > 不懂就看视频哈
12 | - https://www.bilibili.com/video/BV1z54y1e7vE
13 | - https://www.bilibili.com/video/BV1zD4y127vG
14 |
15 | ```
16 | ha_baidu_map:
17 | ak: 百度地图【浏览器的浏览器的浏览器的AK密钥】 - 如果不会配置ak就把这行删掉,初次安装空白就刷新一下页面
18 | ```
19 |
20 | ## 在GPSLogger应用里的配置
21 | ```
22 | http://IP:8123/ha_baidu_map-location-【自动生成的key】?entity_id=【实体ID】&latitude=%LAT&longitude=%LON&battery=%BATT&sts=%STARTTIMESTAMP
23 |
24 | ```
25 |
26 | # 更新日志
27 |
28 | ### v2.4.3
29 | - 修复地图点击后出现地点详情的问题
30 | - 增加设备里的编码地理位置显示
31 | - 隐藏百度地图版权信息
32 | - 调整头部高度样式
33 | - 支持集成添加
34 |
35 | ### v2.4
36 | - 将百度HTTP协议改为HTTPS
37 | - 移除隐藏默认地图功能
38 | - 新增定位数据添加接口
39 | - 修复不显示person实体的问题
40 | - 更换实体名称
41 | - 添加地图卡片
42 | - 不是管理员也能显示百度地图侧边栏
43 |
44 | ### v2.3
45 | - 修复图标不显示的问题
46 | - 右上角新增编辑图标
47 |
48 | ### v2.2
49 | - 修复在HA的APP中无法加载GPS轨迹的问题
50 | - 解决移动端不能点击实体的问题
51 |
52 | ### v2.1
53 | - 修复https下无法加载接口的问题
54 |
55 | ### v2.0
56 | - 修改为本地json文件存储(文件在.storage/ha_baidu_map/)
57 | - 支持多设备分类记录
58 | - 修复设备多次重复记录问题
59 | - 支持配置隐藏自带地图
60 |
61 | ### v1.0.5
62 | - 修复菜单图标一直显示的问题
63 |
64 | ### v1.0.4(测试版)
65 | - 加入数据库存储(目前只支持HA自带的数据库)
66 | - 加入GPS运动轨迹显示
67 |
68 | ### v1.0
69 | - 区域设置passive属性为true会自动隐藏(同官方地图)
70 | - 设备设置hidden属性为true会自动隐藏(同官方地图)
71 | - 修复更新坐标时,会出现重复的问题
72 | - 修复多个区域无法加载的问题
73 | - 修复头像和官方地图显示不一致的问题
74 | - 加入区域可以点击查看的功能(这个官方没有)
75 |
76 |
77 | ## 如果这个项目对你有帮助,请我喝杯咖啡奶茶吧😘
78 | |支付宝|微信|
79 | |---|---|
80 |
|
81 |
--------------------------------------------------------------------------------
/custom_components/ha_baidu_map/__init__.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from homeassistant.helpers.network import get_url
3 | from .const import NAME, ICON, DOMAIN, VERSION, URL, ROOT_PATH
4 |
5 | from .api_storage import ApiStorage
6 | from .api_view import HassGateView, mouted_view
7 |
8 | _LOGGER = logging.getLogger(__name__)
9 |
10 | def setup(hass, config):
11 | # 没有配置和已经运行则不操作
12 | if DOMAIN not in config or DOMAIN in hass.data:
13 | return True
14 |
15 | cfg = config[DOMAIN]
16 | ak = cfg.get("ak", "hNT4WeW0AGvh2GuzuO92OfM6hCW25HhX")
17 | hass.data[DOMAIN] = ApiStorage(hass)
18 | # 定位地址
19 | LOCATION_URL = '/' + DOMAIN + '-location-' + ak[0:5]
20 | # 绝对路径
21 | ABSOLUTE_LOCATION_URL = get_url(hass) + LOCATION_URL
22 |
23 | _LOGGER.info('''
24 | -------------------------------------------------------------------
25 |
26 | 百度地图【作者QQ:635147515】
27 | 版本:''' + VERSION + '''
28 | 定位地址:''' + ABSOLUTE_LOCATION_URL + '''
29 | 项目地址:https://github.com/shaonianzhentan/ha_baidu_map
30 |
31 | -------------------------------------------------------------------''')
32 | # 设置状态
33 | hass.states.async_set('map.baidu', VERSION, {
34 | "friendly_name": NAME, "icon": ICON,
35 | 'api': 'https://api.map.baidu.com/getscript?v=3.0&ak=' + ak,
36 | 'location':ABSOLUTE_LOCATION_URL + '?latitude=%LAT&longitude=%LON&battery=%BATT&sts=%STARTTIMESTAMP&entity_id=实体ID'
37 | })
38 | # 注册静态目录
39 | hass.http.register_static_path(ROOT_PATH, hass.config.path("custom_components/" + DOMAIN + "/local"), False)
40 | hass.components.frontend.add_extra_js_url(hass, ROOT_PATH + '/ha-panel-baidu-map.js')
41 | hass.components.frontend.async_register_built_in_panel(
42 | "baidu-map", NAME, ICON,
43 | frontend_url_path='ha_baidu_map',
44 | config={"ak": ak, "url_path": ROOT_PATH},
45 | require_admin=False)
46 | hass.http.register_view(HassGateView)
47 | mouted_view(hass, LOCATION_URL)
48 | return True
49 |
50 | # 集成安装
51 | async def async_setup_entry(hass, entry):
52 | setup(hass, { DOMAIN: entry.data })
53 | return True
54 |
--------------------------------------------------------------------------------
/custom_components/ha_baidu_map/api_config.py:
--------------------------------------------------------------------------------
1 | import json, os
2 |
3 | class ApiConfig():
4 |
5 | def __init__(self, _dir):
6 | if os.path.exists(_dir) == False:
7 | self.mkdir(_dir)
8 | self.dir = _dir
9 |
10 | # 创建文件夹
11 | def mkdir(self, path):
12 | folders = []
13 | while not os.path.isdir(path):
14 | path, suffix = os.path.split(path)
15 | folders.append(suffix)
16 | for folder in folders[::-1]:
17 | path = os.path.join(path, folder)
18 | os.mkdir(path)
19 |
20 | def get_dirs(self, _path):
21 | file_name = os.listdir(_path)
22 | _list = []
23 | for file in file_name:
24 | abs_path = os.path.join(_path, file)
25 | if os.path.isdir(abs_path):
26 | fileinfo = os.stat(abs_path)
27 | _list.append({
28 | 'name': file,
29 | 'path': abs_path,
30 | 'size': fileinfo.st_size,
31 | 'size_name': self.format_byte(fileinfo.st_size),
32 | 'edit_time': fileinfo.st_mtime,
33 | })
34 | return _list
35 |
36 | def get_files(self, _path):
37 | file_name = os.listdir(_path)
38 | _list = []
39 | for file in file_name:
40 | abs_path = os.path.join(_path, file)
41 | if os.path.isfile(abs_path):
42 | fileinfo = os.stat(abs_path)
43 | _list.append({
44 | 'name': file,
45 | 'path': abs_path,
46 | 'size': fileinfo.st_size,
47 | 'size_name': self.format_byte(fileinfo.st_size),
48 | 'edit_time': fileinfo.st_mtime,
49 | })
50 | return _list
51 |
52 | # 格式化文件大小的函数
53 | def format_byte(self, number):
54 | for (scale, label) in [(1024*1024*1024, "GB"), (1024*1024,"MB"), (1024,"KB")]:
55 | if number >= scale:
56 | return "%.2f %s" %(number*1.0/scale,lable)
57 | elif number == 1:
58 | return "1字节"
59 | else: #小于1字节
60 | byte = "%.2f" % (number or 0)
61 | return ((byte[:-3]) if byte.endswith(".00") else byte) + "字节"
62 |
63 | def get_path(self, name):
64 | return self.dir + '/' + name
65 |
66 | def read(self, name):
67 | fn = self.get_path(name)
68 | if os.path.isfile(fn):
69 | with open(fn,'r', encoding='utf-8') as f:
70 | content = json.load(f)
71 | return content
72 | return None
73 |
74 | def write(self, name, obj):
75 | with open(self.get_path(name), 'w', encoding='utf-8') as f:
76 | json.dump(obj, f, ensure_ascii=False)
--------------------------------------------------------------------------------
/custom_components/ha_baidu_map/api_storage.py:
--------------------------------------------------------------------------------
1 | import time, os
2 | from .api_config import ApiConfig
3 |
4 | class ApiStorage():
5 |
6 | def __init__(self, hass):
7 | self.hass = hass
8 | self.cache_dir = './.shaonianzhentan/ha_baidu_map'
9 | # 初始化文件夹
10 | self.cfg = ApiConfig(hass.config.path(self.cache_dir))
11 |
12 | # 获取所有列表信息
13 | def get_list(self):
14 | _path = self.hass.config.path(self.cache_dir)
15 | _list = []
16 | dirs = self.cfg.get_dirs(_path)
17 | for dir in dirs:
18 | # state = hass.states.get('device_tracker.' + dir['name'])
19 | _list.append({'list': [], 'name': dir['name']})
20 | lastIndex = len(_list) - 1
21 | files = self.cfg.get_files(dir['path'])
22 | for file in files:
23 | # 小于2kb数据直接丢弃
24 | if file['size'] < 2048:
25 | continue
26 | _name = file['name'].replace('.json', '')
27 | _list[lastIndex]['list'].append({'sts': _name, 'cdate': time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(int(_name)))})
28 |
29 | return _list
30 |
31 | # 获取数据信息
32 | def get_info(self, name):
33 | _list = self.cfg.read(name + '.json')
34 | if _list == None:
35 | _list = []
36 | return _list
37 |
38 | # 添加数据
39 | def add(self, entity_id, gps_info):
40 | _dir = self.hass.config.path(self.cache_dir + '/' + entity_id)
41 | if os.path.exists(_dir) == False:
42 | os.mkdir(_dir)
43 | sts_name = entity_id + '/' + gps_info['sts'] + '.json'
44 | _list = self.cfg.read(sts_name)
45 | if _list == None:
46 | _list = []
47 | # 如果本次传入的数据,与最后一次一样,则不记录
48 | _len = len(_list)
49 | if _len > 0:
50 | attr = _list[_len-1]
51 | if attr['latitude'] == gps_info['latitude'] and attr['longitude'] == gps_info['longitude']:
52 | return
53 | # 添加数据
54 | _list.append(gps_info)
55 | # 写入数据
56 | self.cfg.write(sts_name, _list)
57 |
--------------------------------------------------------------------------------
/custom_components/ha_baidu_map/api_view.py:
--------------------------------------------------------------------------------
1 |
2 | import time
3 | from homeassistant.components.http import HomeAssistantView
4 | from .const import DOMAIN, URL
5 |
6 |
7 | def timestamp_to_str(timestamp=None, format='%Y-%m-%d %H:%M:%S'):
8 | if timestamp:
9 | time_tuple = time.localtime(timestamp) # 把时间戳转换成时间元祖
10 | result = time.strftime(format, time_tuple) # 把时间元祖转换成格式化好的时间
11 | return result
12 | else:
13 | return time.strptime(format)
14 |
15 | # 获取信息
16 | class HassGateView(HomeAssistantView):
17 |
18 | url = URL
19 | name = DOMAIN
20 | requires_auth = True
21 |
22 | async def post(self, request):
23 | hass = request.app["hass"]
24 | try:
25 | res = await request.json()
26 |
27 | sql = hass.data[DOMAIN]
28 | if res['type'] == 'get_info':
29 | # 获取同一时刻的GPS记录,生成运动轨迹
30 | _list = sql.get_info(res['sts'])
31 | return self.json(_list)
32 | elif res['type'] == 'get_list':
33 | # 获取 有5条记录 的所有时刻
34 | _list = sql.get_list()
35 | return self.json(_list)
36 | # 删除某些时刻的运动轨迹
37 | # 删除所有数据
38 | return self.json(res)
39 | except Exception as e:
40 | print(e)
41 | return self.json({'code':1, 'msg': '出现异常'})
42 |
43 | # 安装网关
44 | def mouted_view(hass, LOCATION_URL):
45 | # 记录信息
46 | class LocationGateView(HomeAssistantView):
47 | url = LOCATION_URL
48 | name = DOMAIN
49 | requires_auth = False
50 |
51 | async def get(self, request):
52 | query = request.query
53 | print(query)
54 | try:
55 | entity_id = query['entity_id']
56 | latitude = query['latitude']
57 | longitude = query['longitude']
58 | battery = query.get('battery', 0)
59 | sts = query['sts']
60 | entity = hass.states.get(entity_id)
61 | if entity is not None and hasattr(entity, 'attributes'):
62 | attributes = {
63 | **entity.attributes,
64 | 'latitude': latitude,
65 | 'longitude': longitude,
66 | 'battery': battery,
67 | 'sts': sts,
68 | 'sts_date': timestamp_to_str(int(sts)),
69 | }
70 | hass.states.async_set(entity_id, entity.state, attributes)
71 | # 存储定位信息
72 | sql.add(entity_id, attributes)
73 | return self.json({'code':0, 'msg': '定位发送成功'})
74 | except Exception as ex:
75 | print(ex)
76 | return self.json({'code':1, 'msg': '出现异常'})
77 | hass.http.register_view(LocationGateView)
78 |
--------------------------------------------------------------------------------
/custom_components/ha_baidu_map/config_flow.py:
--------------------------------------------------------------------------------
1 | """Config flow for Hello World integration."""
2 | import logging
3 |
4 | import voluptuous as vol
5 |
6 | from homeassistant import config_entries
7 |
8 | from .const import DOMAIN # pylint:disable=unused-import
9 |
10 | _LOGGER = logging.getLogger(__name__)
11 |
12 | DATA_SCHEMA = vol.Schema({vol.Required("ak", default = "hNT4WeW0AGvh2GuzuO92OfM6hCW25HhX"): str})
13 |
14 | class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
15 |
16 | VERSION = 1
17 | CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
18 |
19 | async def async_step_user(self, user_input=None):
20 | errors = {}
21 | if DOMAIN in self.hass.data:
22 | return self.async_abort(reason="single_instance_allowed")
23 |
24 | # 如果输入内容不为空,则进行验证
25 | if user_input is not None:
26 | return self.async_create_entry(title="", data=user_input)
27 |
28 | # 显示表单
29 | return self.async_show_form(
30 | step_id="user", data_schema=DATA_SCHEMA, errors=errors
31 | )
--------------------------------------------------------------------------------
/custom_components/ha_baidu_map/const.py:
--------------------------------------------------------------------------------
1 | NAME = '百度地图'
2 | ICON = 'mdi:map-marker-radius'
3 | DOMAIN = 'ha_baidu_map'
4 | VERSION = '2.4.3'
5 | URL = '/' + DOMAIN + '-api'
6 | ROOT_PATH = '/' + DOMAIN + '-local/' + VERSION
--------------------------------------------------------------------------------
/custom_components/ha_baidu_map/local/ha-panel-baidu-map.js:
--------------------------------------------------------------------------------
1 | function rad(d) {
2 | return d * Math.PI / 180.0;
3 | }
4 | // 根据经纬度计算距离,参数分别为第一点的纬度,经度;第二点的纬度,经度
5 | function getDistance(lat1, lng1, lat2, lng2) {
6 |
7 | var radLat1 = rad(lat1);
8 | var radLat2 = rad(lat2);
9 | var a = radLat1 - radLat2;
10 | var b = rad(lng1) - rad(lng2);
11 | var s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
12 | Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
13 | s = s * 6378.137; // EARTH_RADIUS;
14 | s = Math.round(s * 10000) / 10000; //输出为公里
15 |
16 | var distance = s;
17 | var distance_str = "";
18 |
19 | if (parseInt(distance) >= 1) {
20 | distance_str = distance.toFixed(1) + "km";
21 | } else {
22 | distance_str = distance * 1000 + "m";
23 | }
24 |
25 | //s=s.toFixed(4);
26 |
27 | console.info('lyj 距离是', s);
28 | console.info('lyj 距离是', distance_str);
29 | //return s;
30 | return distance_str
31 | }
32 |
33 | class HaPanelBaiduMap extends HTMLElement {
34 | constructor() {
35 | super()
36 | const shadow = this.attachShadow({ mode: 'open' });
37 | const div = document.createElement('div', { 'class': 'root' });
38 | div.innerHTML = `
39 |
40 |
41 |
42 |
43 |
44 |
73 | `
74 |
75 | shadow.appendChild(div)
76 | this.shadow = shadow
77 | this.$ = shadow.querySelector.bind(shadow)
78 | }
79 |
80 | // 获取位置
81 | getPosition(attr) {
82 | if (('longitude' in attr && 'latitude' in attr) || 'Longitude' in attr && 'Latitude' in attr) {
83 | return {
84 | longitude: attr.longitude || attr.Longitude,
85 | latitude: attr.latitude || attr.Latitude
86 | }
87 | } else {
88 | return null
89 | }
90 | }
91 |
92 | ready() {
93 | if (window.BMap) {
94 | // 添加标题
95 | let toolbar = this.shadow.querySelector('app-toolbar')
96 | if (toolbar.children.length === 0) {
97 | // console.log(window.BMap)
98 | // console.log('%O',this)
99 | // console.log('%O',toolbar)
100 | top.ha_hass = this.hass
101 | // 添加菜单
102 | let menuButton = document.createElement('ha-menu-button')
103 | menuButton.hass = this.hass
104 | menuButton.narrow = true
105 | toolbar.appendChild(menuButton)
106 | // 添加标题
107 | let title = document.createElement('div')
108 | title.setAttribute('main-title', '')
109 | title.textContent = '百度地图'
110 | title.style.marginLeft = '20px'
111 | toolbar.appendChild(title)
112 | // 添加编辑图标
113 | let haIconButton = document.createElement('ha-icon-button')
114 | haIconButton.setAttribute('icon', 'hass:pencil')
115 | haIconButton.setAttribute('title', '编辑位置')
116 | haIconButton.onclick = () => {
117 | history.pushState(null, null, '/config/zone')
118 | this.fire('location-changed')
119 | }
120 | toolbar.appendChild(haIconButton)
121 | // 添加运动轨迹图标
122 | let haIconButton2 = document.createElement('ha-icon-button')
123 | haIconButton2.setAttribute('icon', 'mdi:crosshairs-gps')
124 | haIconButton2.setAttribute('title', 'GPS运动轨迹')
125 | haIconButton2.onclick = () => {
126 | let gg = this.$('#gps')
127 | gg.src = `${this.url_path}/travel.html?r=${Date.now()}`
128 | gg.classList.toggle('hide')
129 | }
130 | toolbar.appendChild(haIconButton2)
131 | // 添加人员图标
132 | let haIconButton3 = document.createElement('ha-icon-button')
133 | haIconButton3.setAttribute('icon', 'mdi:account-outline')
134 | haIconButton3.setAttribute('title', '定位设备')
135 | haIconButton3.onclick = () => {
136 | let dialog = document.createElement('div')
137 | // 生成遮罩
138 | let mask = document.createElement('div')
139 | mask.style = 'height:100vh;width:100vw;position:fixed;top:0;left:0;background:rgba(0,0,0,.5);'
140 | dialog.appendChild(mask)
141 |
142 | let fm = document.createDocumentFragment()
143 | // 生成标题
144 | let title = document.createElement('div')
145 | title.style = `text-align: right; padding: 15px;`
146 | title.innerHTML = ``
147 | title.onclick = () => {
148 | dialog.remove()
149 | }
150 | fm.appendChild(title)
151 | // 生成设备表格
152 | let table = document.createElement('table')
153 | table.border = true
154 | table.style = 'width:90%;border-spacing: 1px;text-align:left; border-collapse: collapse;margin: 0 auto;'
155 | table.borderSpacing = 10
156 | let tr = document.createElement('tr')
157 | tr.innerHTML = `
158 | 实体ID |
159 | 设备 |
160 | 距离 |
161 | 操作 | `
162 | table.appendChild(tr)
163 |
164 | let states = this.hass.states
165 | // 获取家的坐标
166 | let homeAttr = states['zone.home']['attributes']
167 | let homelatitude = homeAttr['latitude']
168 | let homelongitude = homeAttr['longitude']
169 | // 获取所有定位设备
170 | let keys = Object.keys(states).filter(ele =>
171 | ['device_tracker', 'zone', 'person'].includes(ele.split('.')[0])
172 | || ele.includes('di_li_bian_ma_wei_zhi'))
173 | keys.forEach(key => {
174 | let stateObj = states[key]
175 | let attr = stateObj.attributes
176 | let pos = this.getPosition(attr)
177 | if (pos) {
178 | let { longitude, latitude } = pos
179 | tr = document.createElement('tr')
180 | let valueArr = [key, attr.friendly_name, getDistance(latitude, longitude, homelatitude, homelongitude)]
181 | valueArr.forEach(value => {
182 | let td = document.createElement('td')
183 | td.textContent = value
184 | tr.appendChild(td)
185 | })
186 | // 操作
187 | let td = document.createElement('td')
188 | td.textContent = '选择'
189 | td.style = 'cursor:pointer; text-align:center;'
190 | td.onclick = () => {
191 | this.translate({ longitude, latitude }).then(res => {
192 | map.centerAndZoom(res[0], 18);
193 | })
194 | dialog.remove()
195 | }
196 | tr.appendChild(td)
197 | table.appendChild(tr)
198 | }
199 | })
200 | fm.appendChild(table)
201 |
202 | let panel = document.createElement('div')
203 | panel.style = `height:80vh;width:80vw;left:10vw;top:10vh;position:absolute;background:white;overflow:auto;`
204 | panel.appendChild(fm)
205 | dialog.appendChild(panel)
206 |
207 | document.body.appendChild(dialog)
208 | }
209 | toolbar.appendChild(haIconButton3)
210 |
211 | // 获取当前HA配置的经纬度
212 | let { latitude, longitude } = this.hass.config
213 | // 生成一个对象
214 | var map = new BMap.Map(this.shadow.querySelector('#baidu-map'));
215 | this.map = map
216 | //添加地图类型控件
217 | map.addControl(new BMap.MapTypeControl({
218 | mapTypes: [
219 | BMAP_NORMAL_MAP,
220 | BMAP_HYBRID_MAP
221 | ]
222 | }))
223 | // 启用鼠标滚轮缩放
224 | map.enableScrollWheelZoom();
225 |
226 | // this.actionPanel()
227 |
228 | this.translate({ longitude, latitude }).then(res => {
229 | let mPoint = res[0]
230 | // 中心点
231 | map.centerAndZoom(mPoint, 18);
232 | })
233 |
234 | this.loadZone()
235 | //this.update()
236 | /*
237 | var top_left_control = new BMap.ScaleControl({anchor: BMAP_ANCHOR_TOP_LEFT});// 左上角,添加比例尺
238 | var top_left_navigation = new BMap.NavigationControl(); //左上角,添加默认缩放平移控件
239 | var top_right_navigation = new BMap.NavigationControl({anchor: BMAP_ANCHOR_TOP_RIGHT, type: BMAP_NAVIGATION_CONTROL_SMALL}); //右上角,仅包含平移和缩放按钮
240 | mp.addControl(top_left_control);
241 | mp.addControl(top_left_navigation);
242 | mp.addControl(top_right_navigation);
243 | */
244 | //触摸事件(解决点击事件无效)--触摸开始,开启拖拽
245 | map.addEventListener('touchmove', function (e) {
246 | map.enableDragging();
247 | });
248 | //触摸结束始,禁止拖拽
249 | map.addEventListener("touchend", function (e) {
250 | map.disableDragging();
251 | });
252 | } else {
253 | this.loadDevice()
254 | }
255 | }
256 | }
257 |
258 | /**************************** 加载区域 ********************************/
259 |
260 | // 添加Icon标记
261 | addIconMarker(point, icon, key) {
262 | let _this = this
263 | const map = this.map
264 | // 复杂的自定义覆盖物
265 | function ComplexCustomOverlay() { }
266 |
267 | ComplexCustomOverlay.prototype = new BMap.Overlay();
268 | ComplexCustomOverlay.prototype.initialize = function (map) {
269 | this._map = map;
270 | var div = this._div = document.createElement("div");
271 | div.style.position = "absolute";
272 | div.style.zIndex = BMap.Overlay.getZIndex(point.lat);
273 | div.style.MozUserSelect = "none";
274 | div.innerHTML = ``
275 | map.getPanes().labelPane.appendChild(div);
276 | div.onclick = function () {
277 | _this.fire('hass-more-info', { entityId: key })
278 | }
279 | return div;
280 | }
281 | ComplexCustomOverlay.prototype.draw = function () {
282 | var pixel = map.pointToOverlayPixel(point);
283 | this._div.style.left = pixel.x - 12 + "px";
284 | this._div.style.top = pixel.y - 12 + "px";
285 | }
286 | var myCompOverlay = new ComplexCustomOverlay();
287 |
288 | map.addOverlay(myCompOverlay);
289 | }
290 |
291 | //加载区域
292 | loadZone() {
293 | let map = this.map
294 | this.debounce(async () => {
295 | // 这里添加设备
296 | let states = this.hass.states
297 | let keys = Object.keys(states).filter(ele => ele.indexOf('zone') === 0)
298 | for (let key of keys) {
299 | let stateObj = states[key]
300 | let attr = stateObj.attributes
301 | // 如果有经纬度,并且不在家,则标记
302 | if (!attr['passive'] && 'longitude' in attr && 'latitude' in attr) {
303 | let res = await this.translate({ longitude: attr.longitude, latitude: attr.latitude })
304 | let point = res[0]
305 | // 添加圆形区域
306 | var circle = new BMap.Circle(point, attr.radius, { fillColor: "#FF9800", strokeColor: 'orange', strokeWeight: 1, fillOpacity: 0.3, strokeOpacity: 0.5 });
307 | map.addOverlay(circle);
308 | // 添加图标
309 | this.addIconMarker(point, attr.icon, key)
310 | }
311 | }
312 | // 加载完区域之后
313 | this.loadDevice()
314 | }, 1000)
315 | }
316 |
317 | /**************************** 加载设备 ********************************/
318 |
319 | // 添加设备标记
320 | addEntityMarker(point, { id, name, picture }) {
321 | let _this = this
322 | const map = this.map
323 |
324 | // 删除所有设备
325 | let allOverlay = map.getOverlays();
326 | if (allOverlay.length > 0) {
327 | let index = allOverlay.findIndex(ele => ele['id'] === id)
328 | if (index >= 0) {
329 | map.removeOverlay(allOverlay[index]);
330 | }
331 | }
332 | // console.log(allOverlay)
333 | setTimeout(() => {
334 | // 复杂的自定义覆盖物
335 | function ComplexCustomOverlay() { }
336 |
337 | ComplexCustomOverlay.prototype = new BMap.Overlay();
338 | ComplexCustomOverlay.prototype.initialize = function (map) {
339 | this._map = map;
340 | var div = this._div = document.createElement("div");
341 | div.className = "device-marker";
342 | div.style.zIndex = BMap.Overlay.getZIndex(point.lat);
343 | // console.log(id,name,picture)
344 | if (picture) {
345 | div.style.backgroundImage = `url(${picture})`
346 | div.style.backgroundSize = 'cover'
347 | } else {
348 | div.textContent = name[0]
349 | }
350 | div.onclick = function () {
351 | _this.fire('hass-more-info', { entityId: id })
352 | }
353 | map.getPanes().labelPane.appendChild(div);
354 | return div;
355 | }
356 | ComplexCustomOverlay.prototype.draw = function () {
357 | var pixel = map.pointToOverlayPixel(point);
358 | this._div.style.left = pixel.x - 28 + "px";
359 | this._div.style.top = pixel.y - 28 + "px";
360 | }
361 | var myCompOverlay = new ComplexCustomOverlay();
362 | myCompOverlay.id = id
363 | map.addOverlay(myCompOverlay);
364 | }, 0)
365 | }
366 |
367 | // 更新位置
368 | loadDevice() {
369 | this.debounce(async () => {
370 | // 这里添加设备
371 | let states = this.hass.states
372 | let keys = Object.keys(states).filter(ele => ele.indexOf('device_tracker') === 0
373 | || ele.indexOf('person') === 0
374 | || ele.includes('di_li_bian_ma_wei_zhi'))
375 | for (let key of keys) {
376 | let stateObj = states[key]
377 | let attr = stateObj.attributes
378 | let pos = this.getPosition(attr)
379 | if (pos) {
380 | // 如果有经纬度,并且不在家,则标记
381 | if (!attr['hidden'] && stateObj.state != 'home') {
382 | let res = await this.translate(pos)
383 | let point = res[0]
384 | this.addEntityMarker(point, {
385 | id: key,
386 | name: attr.friendly_name,
387 | picture: attr['entity_picture']
388 | })
389 | }
390 | }
391 |
392 | }
393 | }, 1000)
394 | }
395 |
396 | // 坐标转换
397 | translate({ longitude, latitude }) {
398 | return new Promise((resolve, reject) => {
399 | var points = [new BMap.Point(longitude, latitude)]
400 | var convertor = new BMap.Convertor();
401 | convertor.translate(points, 1, 5, function (data) {
402 | if (data.status === 0) {
403 | resolve(data.points)
404 | }
405 | })
406 | })
407 | }
408 |
409 | // 触发事件
410 | fire(type, data) {
411 | const event = new Event(type, {
412 | bubbles: true,
413 | cancelable: false,
414 | composed: true
415 | });
416 | event.detail = data;
417 | this.dispatchEvent(event);
418 | }
419 |
420 | // 操作面板
421 | actionPanel() {
422 |
423 | // 获取所有设备
424 | this.deviceList = []
425 |
426 | let states = this.hass.states
427 | let keys = Object.keys(states).filter(ele => ele.indexOf('device_tracker') === 0
428 | || ele.indexOf('zone') === 0
429 | || ele.indexOf('person') === 0)
430 | keys.forEach(key => {
431 | let stateObj = states[key]
432 | let attr = stateObj.attributes
433 | if (('longitude' in attr && 'latitude' in attr) || 'Longitude' in attr && 'Latitude' in attr) {
434 | this.deviceList.push({
435 | id: key,
436 | name: attr.friendly_name,
437 | longitude: attr.longitude || attr.Longitude,
438 | latitude: attr.latitude || attr.Latitude
439 | })
440 | }
441 | })
442 |
443 | const map = this.map
444 | // 定义一个控件类,即function
445 | function ZoomControl() {
446 | // 默认停靠位置和偏移量
447 | this.defaultAnchor = BMAP_ANCHOR_TOP_LEFT;
448 | this.defaultOffset = new BMap.Size(10, 10);
449 | }
450 |
451 | // 通过JavaScript的prototype属性继承于BMap.Control
452 | ZoomControl.prototype = new BMap.Control();
453 |
454 | // 自定义控件必须实现自己的initialize方法,并且将控件的DOM元素返回
455 | // 在本方法中创建个div元素作为控件的容器,并将其添加到地图容器中
456 | ZoomControl.prototype.initialize = (map) => {
457 | // 创建一个DOM元素
458 | var div = document.createElement("div");
459 | div.className = 'right-action-panel'
460 | div.style.cssText = `background:white;font-size:12px;`
461 | let select = document.createElement('select')
462 | select.className = "select-device"
463 |
464 | // 设备
465 | let optgroup = document.createElement('optgroup')
466 | optgroup.label = "设备"
467 | this.deviceList.forEach(ele => {
468 | let option = document.createElement('option')
469 | option.value = ele.id
470 | option.text = ele.name
471 | optgroup.appendChild(option)
472 | })
473 | select.appendChild(optgroup)
474 |
475 | select.onchange = () => {
476 | // 这里重新定位
477 | if (select.selectedIndex < this.deviceList.length) {
478 | let { longitude, latitude } = this.deviceList[select.selectedIndex]
479 | this.translate({ longitude, latitude }).then(res => {
480 | map.centerAndZoom(res[0], 18);
481 | })
482 | }
483 | }
484 |
485 | // 添加文字说明
486 | div.appendChild(select);
487 | // 添加DOM元素到地图中
488 | map.getContainer().appendChild(div);
489 | // 将DOM元素返回
490 | return div;
491 | }
492 | // 创建控件
493 | var myZoomCtrl = new ZoomControl();
494 | // 添加到地图当中
495 | map.addControl(myZoomCtrl);
496 |
497 | }
498 |
499 |
500 | /**
501 | * 防抖
502 | * @param {Function} fn
503 | * @param {Number} wait
504 | */
505 | debounce(fn, wait) {
506 | let cache = this.cache || {}
507 | let fnKey = fn.toString()
508 | let timeout = cache[fnKey]
509 | if (timeout != null) clearTimeout(timeout)
510 | cache[fnKey] = setTimeout(() => {
511 | fn()
512 | // 清除内存占用
513 | if (Object.keys(cache).length === 0) {
514 | this.cache = null
515 | } else {
516 | delete this.cache[fnKey]
517 | }
518 | }, wait)
519 | this.cache = cache
520 | }
521 |
522 |
523 | loadScript(src) {
524 | return new Promise((resolve, reject) => {
525 | let id = btoa(src)
526 | let ele = document.getElementById(id)
527 | if (ele) {
528 | resolve()
529 | return
530 | }
531 | let script = document.createElement('script')
532 | script.id = id
533 | script.src = src
534 | script.onload = function () {
535 | resolve()
536 | }
537 | document.querySelector('head').appendChild(script)
538 | })
539 | }
540 |
541 | set narrow(value) {
542 | let menuButton = this.shadow.querySelector('ha-menu-button')
543 | if (menuButton) {
544 | menuButton.hass = this.hass
545 | menuButton.narrow = value
546 | }
547 | }
548 |
549 | get panel() {
550 | return this._panel
551 | }
552 |
553 | set panel(value) {
554 | this._panel = value
555 | let { ak, url_path } = value.config
556 | this.url_path = url_path
557 | if (ak) {
558 | const _this = this
559 | window.BMap_HaBaiduMap = {
560 | url: `https://api.map.baidu.com/getscript?v=3.0&ak=${ak}`,
561 | close() {
562 | let { $ } = _this
563 | $('#gps').classList.toggle('hide')
564 | }
565 | }
566 | this.loadScript(BMap_HaBaiduMap.url).then(res => {
567 | this.ready()
568 | })
569 | } else {
570 | alert('请配置百度AK')
571 | }
572 | }
573 | }
574 |
575 |
576 | /* ----------------------------- 卡版 ----------------------------------- */
577 | class LovelaceBaiduMap extends HTMLElement {
578 |
579 | static getConfigElement() {
580 | return document.createElement("lovelace-baidu-map-editor");
581 | }
582 |
583 | // 自定义默认配置
584 | static getStubConfig() {
585 | return { entity: "zone.home" }
586 | }
587 |
588 | /*
589 | * 设置配置信息
590 | */
591 | setConfig(config) {
592 | if (!config.entity) {
593 | throw new Error('你需要定义一个实体');
594 | }
595 | this._config = config;
596 | // 更新
597 | this.updated()
598 | }
599 |
600 | // 卡片的高度(1 = 50px)
601 | getCardSize() {
602 | return 3;
603 | }
604 |
605 | /*
606 | * 触发事件
607 | * type: 事件名称
608 | * data: 事件参数
609 | */
610 | fire(type, data) {
611 | const event = new Event(type, {
612 | bubbles: true,
613 | cancelable: false,
614 | composed: true
615 | });
616 | event.detail = data;
617 | this.dispatchEvent(event);
618 | }
619 |
620 | /*
621 | * 调用服务
622 | * service: 服务名称(例:light.toggle)
623 | * service_data:服务数据(例:{ entity_id: "light.xiao_mi_deng_pao" } )
624 | */
625 | callService(service_name, service_data = {}) {
626 | let arr = service_name.split('.')
627 | let domain = arr[0]
628 | let service = arr[1]
629 | this._hass.callService(domain, service, service_data)
630 | }
631 |
632 | // 通知
633 | toast(message) {
634 | this.fire("hass-notification", { message })
635 | }
636 |
637 | // 显示实体更多信息
638 | showMoreInfo(entityId) {
639 | this.fire('hass-more-info', { entityId })
640 | }
641 |
642 | /*
643 | * 接收HA核心对象
644 | */
645 | set hass(hass) {
646 | this._hass = hass
647 | if (this.isCreated === true) {
648 | this.updated(hass)
649 | } else {
650 | this.created(hass)
651 | }
652 | }
653 |
654 | loadScript(src) {
655 | return new Promise((resolve, reject) => {
656 | let id = btoa(src)
657 | let ele = document.getElementById(id)
658 | if (ele) {
659 | resolve()
660 | return
661 | }
662 | let script = document.createElement('script')
663 | script.id = id
664 | script.src = src
665 | script.onload = function () {
666 | resolve()
667 | }
668 | document.querySelector('head').appendChild(script)
669 | })
670 | }
671 |
672 | // 创建界面
673 | created(hass) {
674 |
675 | /* ***************** 基础代码 ***************** */
676 | const shadow = this.attachShadow({ mode: 'open' });
677 | // 创建面板
678 | const ha_card = document.createElement('ha-card');
679 | ha_card.className = 'lovelace-baidu-map'
680 | ha_card.innerHTML = ``
681 | shadow.appendChild(ha_card)
682 | // 创建样式
683 | const style = document.createElement('style')
684 | style.textContent = `
685 | .lovelace-baidu-map{}
686 | .device-marker{
687 | position:absolute;
688 | vertical-align: top;
689 | display: block;
690 | margin: 0 auto;
691 | width: 2.5em;
692 | text-align: center;
693 | height: 2.5em;
694 | line-height: 2.5em;
695 | font-size: 1.5em;
696 | border-radius: 50%;
697 | border: 0.1em solid var(--primary-color);
698 | color: rgb(76, 76, 76);
699 | background-color: white;
700 | }
701 | .hide,
702 | .BMap_shadow,.anchorBL,.BMap_cpyCtrl,.BMap_pop{display:none!important;}
703 | `
704 | shadow.appendChild(style);
705 | // 保存核心DOM对象
706 | this.shadow = shadow
707 | this.$ = this.shadow.querySelector.bind(this.shadow)
708 | // 创建成功
709 | this.isCreated = true
710 |
711 | /* ***************** 附加代码 ***************** */
712 | let { _config, $ } = this
713 | // 获取百度API
714 | let baiduApi = hass.states['map.baidu'].attributes['api']
715 | this.loadScript(baiduApi).then(() => {
716 | setTimeout(() => {
717 | this.ready()
718 | }, 1000)
719 | })
720 | }
721 |
722 | // 更新界面数据
723 | updated(hass) {
724 | let { $, _config, update_map } = this
725 | if (update_map) update_map();
726 | }
727 |
728 | // 百度地图加载
729 | ready() {
730 | if (window.BMap) {
731 | // 生成一个对象
732 | let div = this.shadow.querySelector('.lovelace-baidu-map')
733 | div.style.height = div.offsetWidth + 'px'
734 | var map = new BMap.Map(div);
735 | this.map = map
736 | //添加地图类型控件
737 | map.addControl(new BMap.MapTypeControl({
738 | mapTypes: [
739 | BMAP_NORMAL_MAP,
740 | BMAP_HYBRID_MAP
741 | ]
742 | }))
743 | // 启用鼠标滚轮缩放
744 | map.enableScrollWheelZoom();
745 | //触摸事件(解决点击事件无效)--触摸开始,开启拖拽
746 | map.addEventListener('touchmove', function (e) {
747 | map.enableDragging();
748 | });
749 | //触摸结束始,禁止拖拽
750 | map.addEventListener("touchend", function (e) {
751 | map.disableDragging();
752 | });
753 | // 添加区域
754 | this.loadZone()
755 |
756 | // 更新地图
757 | this.update_map = () => {
758 | let entity_id = this._config['entity']
759 | let { longitude, latitude, friendly_name, entity_picture, icon, radius } = this._hass.states[entity_id]['attributes']
760 | this.translate({ longitude, latitude }).then(res => {
761 | let mPoint = res[0]
762 | // 中心点
763 | map.centerAndZoom(mPoint, 18);
764 | if (entity_id.includes('zone.')) return;
765 | this.addEntityMarker(mPoint, {
766 | id: entity_id,
767 | name: friendly_name,
768 | picture: entity_picture
769 | })
770 | })
771 | }
772 | this.update_map()
773 | }
774 | }
775 |
776 | //加载区域
777 | loadZone() {
778 | let map = this.map
779 | this.debounce(async () => {
780 | // 这里添加设备
781 | let states = this._hass.states
782 | let keys = Object.keys(states).filter(ele => ele.indexOf('zone') === 0)
783 | for (let key of keys) {
784 | let stateObj = states[key]
785 | let attr = stateObj.attributes
786 | // 如果有经纬度,并且不在家,则标记
787 | if (!attr['passive'] && 'longitude' in attr && 'latitude' in attr) {
788 | let res = await this.translate({ longitude: attr.longitude, latitude: attr.latitude })
789 | let point = res[0]
790 | // 添加圆形区域
791 | var circle = new BMap.Circle(point, attr.radius, { fillColor: "#FF9800", strokeColor: 'orange', strokeWeight: 1, fillOpacity: 0.3, strokeOpacity: 0.5 });
792 | map.addOverlay(circle);
793 | // 添加图标
794 | this.addIconMarker(point, attr.icon, key)
795 | }
796 | }
797 | }, 1000)
798 | }
799 |
800 | // 添加Icon标记
801 | addIconMarker(point, icon, key) {
802 | let _this = this
803 | const map = this.map
804 | // 复杂的自定义覆盖物
805 | function ComplexCustomOverlay() { }
806 |
807 | ComplexCustomOverlay.prototype = new BMap.Overlay();
808 | ComplexCustomOverlay.prototype.initialize = function (map) {
809 | this._map = map;
810 | var div = this._div = document.createElement("div");
811 | div.style.position = "absolute";
812 | div.style.zIndex = BMap.Overlay.getZIndex(point.lat);
813 | div.style.MozUserSelect = "none";
814 | div.innerHTML = ``
815 | map.getPanes().labelPane.appendChild(div);
816 | div.onclick = function () {
817 | _this.fire('hass-more-info', { entityId: key })
818 | }
819 | return div;
820 | }
821 | ComplexCustomOverlay.prototype.draw = function () {
822 | var pixel = map.pointToOverlayPixel(point);
823 | this._div.style.left = pixel.x - 12 + "px";
824 | this._div.style.top = pixel.y - 12 + "px";
825 | }
826 | var myCompOverlay = new ComplexCustomOverlay();
827 |
828 | map.addOverlay(myCompOverlay);
829 | }
830 |
831 | // 添加设备标记
832 | addEntityMarker(point, { id, name, picture }) {
833 | let _this = this
834 | const map = this.map
835 |
836 | // 删除所有设备
837 | let allOverlay = map.getOverlays();
838 | if (allOverlay.length > 0) {
839 | let index = allOverlay.findIndex(ele => ele['id'] === id)
840 | if (index >= 0) {
841 | map.removeOverlay(allOverlay[index]);
842 | }
843 | }
844 | // console.log(allOverlay)
845 | setTimeout(() => {
846 | // 复杂的自定义覆盖物
847 | function ComplexCustomOverlay() { }
848 |
849 | ComplexCustomOverlay.prototype = new BMap.Overlay();
850 | ComplexCustomOverlay.prototype.initialize = function (map) {
851 | this._map = map;
852 | var div = this._div = document.createElement("div");
853 | div.className = "device-marker";
854 | div.style.zIndex = BMap.Overlay.getZIndex(point.lat);
855 | // console.log(id, name, picture)
856 | if (picture) {
857 | div.style.backgroundImage = `url(${picture})`
858 | div.style.backgroundSize = 'cover'
859 | } else {
860 | div.textContent = name[0]
861 | }
862 | div.onclick = function () {
863 | _this.fire('hass-more-info', { entityId: id })
864 | }
865 | map.getPanes().labelPane.appendChild(div);
866 | return div;
867 | }
868 | ComplexCustomOverlay.prototype.draw = function () {
869 | var pixel = map.pointToOverlayPixel(point);
870 | this._div.style.left = pixel.x - 28 + "px";
871 | this._div.style.top = pixel.y - 28 + "px";
872 | }
873 | var myCompOverlay = new ComplexCustomOverlay();
874 | myCompOverlay.id = id
875 | map.addOverlay(myCompOverlay);
876 | }, 0)
877 | }
878 |
879 | // 坐标转换
880 | translate({ longitude, latitude }) {
881 | return new Promise((resolve, reject) => {
882 | var points = [new BMap.Point(longitude, latitude)]
883 | var convertor = new BMap.Convertor();
884 | convertor.translate(points, 1, 5, function (data) {
885 | if (data.status === 0) {
886 | resolve(data.points)
887 | }
888 | })
889 | })
890 | }
891 |
892 |
893 | /**
894 | * 防抖
895 | * @param {Function} fn
896 | * @param {Number} wait
897 | */
898 | debounce(fn, wait) {
899 | let cache = this.cache || {}
900 | let fnKey = fn.toString()
901 | let timeout = cache[fnKey]
902 | if (timeout != null) clearTimeout(timeout)
903 | cache[fnKey] = setTimeout(() => {
904 | fn()
905 | // 清除内存占用
906 | if (Object.keys(cache).length === 0) {
907 | this.cache = null
908 | } else {
909 | delete this.cache[fnKey]
910 | }
911 | }, wait)
912 | this.cache = cache
913 | }
914 | }
915 |
916 |
917 | /* ******************** 编辑预览 *************************** */
918 |
919 | // 编辑预览
920 | class LovelaceBaiduMapEditor extends HTMLElement {
921 |
922 | setConfig(config) {
923 | // console.log('预览配置', config)
924 | this._config = config;
925 | }
926 |
927 | configChanged(newConfig) {
928 | const event = new Event("config-changed", {
929 | bubbles: true,
930 | composed: true
931 | });
932 | event.detail = { config: newConfig };
933 | // console.log('更新预览配置', newConfig)
934 | this.dispatchEvent(event);
935 | }
936 |
937 | /*
938 | * 接收HA核心对象
939 | */
940 | set hass(hass) {
941 | this._hass = hass
942 | if (this.isCreated === true) {
943 | this.updated(hass)
944 | } else {
945 | this.created(hass)
946 | }
947 | }
948 |
949 | // 创建界面
950 | created(hass) {
951 | /* ***************** 基础代码 ***************** */
952 | const shadow = this.attachShadow({ mode: 'open' });
953 |
954 | let arr = [], list = [], states = hass.states
955 | Object.keys(states).filter(k => {
956 | // return true
957 | // 过滤设备
958 | if (k.indexOf('person') === 0 || k.indexOf('device_tracker') === 0) {
959 | let attributes = states[k]['attributes']
960 | return Reflect.has(attributes, 'latitude') && Reflect.has(attributes, 'longitude')
961 | }
962 | return false
963 | }).forEach(k => {
964 | list.push(states[k])
965 | arr.push(`
966 | ${states[k]['attributes']['friendly_name'] || ''}
967 | ${k}
968 | `)
969 | })
970 | // 创建面板
971 | const ha_card = document.createElement('ha-card');
972 | ha_card.className = 'lovelace-baidu-map-editor'
973 | ha_card.innerHTML = `
974 |
975 |
976 | ${arr.join('')}
977 |
978 |
979 | `
980 | shadow.appendChild(ha_card)
981 | // 创建样式
982 | const style = document.createElement('style')
983 | style.textContent = `
984 | .lovelace-baidu-map-editor{padding:20px;}
985 | .lovelace-baidu-map-editor paper-item-body{
986 | padding:5px 10px;
987 | display:block;
988 | border-bottom:1px solid white;
989 | }
990 | `
991 | shadow.appendChild(style);
992 | // 保存核心DOM对象
993 | this.shadow = shadow
994 | this.$ = this.shadow.querySelector.bind(this.shadow)
995 | // 创建成功
996 | this.isCreated = true
997 |
998 | /* ***************** 附加代码 ***************** */
999 | let { _config, $ } = this
1000 | let _this = this
1001 | // // 定义事件
1002 | $('.lovelace-baidu-map-editor paper-listbox').addEventListener('selected-changed', function () {
1003 | let obj = list[this.selected]
1004 | _this.configChanged({
1005 | type: 'custom:lovelace-baidu-map',
1006 | entity: obj.entity_id
1007 | })
1008 | })
1009 | }
1010 |
1011 | // 更新界面数据
1012 | updated(hass) {
1013 | let { $, _config } = this
1014 | // $('p').textContent = `当前实体ID:${_config.entity}`
1015 | }
1016 | }
1017 |
1018 |
1019 | // 百度地图使用HTTPS协议
1020 | window.BMAP_PROTOCOL = "https"
1021 | window.BMap_loadScriptTime = (new Date).getTime()
1022 |
1023 | // 定义DOM对象元素
1024 | if (!customElements.get('ha-panel-baidu-map')) customElements.define('ha-panel-baidu-map', HaPanelBaiduMap);
1025 | if (!customElements.get('lovelace-baidu-map')) customElements.define('lovelace-baidu-map', LovelaceBaiduMap);
1026 | if (!customElements.get('lovelace-baidu-map-editor')) customElements.define('lovelace-baidu-map-editor', LovelaceBaiduMapEditor);
1027 |
1028 | // 添加预览
1029 | window.customCards = window.customCards || [];
1030 | window.customCards.push({
1031 | type: "lovelace-baidu-map",
1032 | name: "百度地图",
1033 | preview: true,
1034 | description: "百度地图卡片"
1035 | });
--------------------------------------------------------------------------------
/custom_components/ha_baidu_map/local/travel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
52 | GPS运动轨迹
53 |
54 |
55 |
56 |
59 | 关闭
60 |
61 |
62 |
233 |
234 |
235 |
236 |
237 |
--------------------------------------------------------------------------------
/custom_components/ha_baidu_map/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "domain": "ha_baidu_map",
3 | "name": "\u767E\u5EA6\u5730\u56FE",
4 | "version": "1.0",
5 | "config_flow": true,
6 | "documentation": "https://github.com/shaonianzhentan/ha_baidu_map",
7 | "requirements": [],
8 | "dependencies": [],
9 | "codeowners": []
10 | }
11 |
--------------------------------------------------------------------------------
/hacs.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "百度地图",
3 | "country": "CN",
4 | "render_readme": true,
5 | "domains": []
6 | }
--------------------------------------------------------------------------------