init
This commit is contained in:
21
LICENSE.TXT
Normal file
21
LICENSE.TXT
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 QmiTek (https://qmikeji.cn)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
26
config.json
Normal file
26
config.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": {
|
||||||
|
"zh-cn": "makergen开发工具",
|
||||||
|
"en": "makergen SDK"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"zh-cn": "makergen开发工具。允许用户连接makergen页面",
|
||||||
|
"en": "makergen SDK for makergen users"
|
||||||
|
},
|
||||||
|
"author": "QmiTek",
|
||||||
|
"email": "qmi@qmikeji.cn",
|
||||||
|
"license": "MIT",
|
||||||
|
"isBoard": false,
|
||||||
|
"id": "makergen",
|
||||||
|
"platform": ["win","mac","web","linux"],
|
||||||
|
"version": "0.0.1",
|
||||||
|
"asset": {
|
||||||
|
"python": {
|
||||||
|
"dir": "python/",
|
||||||
|
"main": "main.ts",
|
||||||
|
"dependencies": {
|
||||||
|
"websocket-client":"1.9.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
python/_images/featured.png
Normal file
BIN
python/_images/featured.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 126 KiB |
26
python/_images/icon.svg
Normal file
26
python/_images/icon.svg
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
|
||||||
|
|
||||||
|
<!-- 齿轮 -->
|
||||||
|
<g transform="translate(256,256)" fill="#FFFFFF">
|
||||||
|
<rect x="-13" y="-92" width="26" height="28" rx="5"/>
|
||||||
|
<rect x="-13" y="-92" width="26" height="28" rx="5" transform="rotate(45)"/>
|
||||||
|
<rect x="-13" y="-92" width="26" height="28" rx="5" transform="rotate(90)"/>
|
||||||
|
<rect x="-13" y="-92" width="26" height="28" rx="5" transform="rotate(135)"/>
|
||||||
|
<rect x="-13" y="-92" width="26" height="28" rx="5" transform="rotate(180)"/>
|
||||||
|
<rect x="-13" y="-92" width="26" height="28" rx="5" transform="rotate(225)"/>
|
||||||
|
<rect x="-13" y="-92" width="26" height="28" rx="5" transform="rotate(270)"/>
|
||||||
|
<rect x="-13" y="-92" width="26" height="28" rx="5" transform="rotate(315)"/>
|
||||||
|
<circle r="68"/>
|
||||||
|
<circle r="36" fill="#0F172A"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- 字母 M(path 精确居中于 256,256) -->
|
||||||
|
<path d="M232,272 L244,242 L256,266 L268,242 L280,272 Z" fill="#FFFFFF"/>
|
||||||
|
|
||||||
|
<!-- 创意火花 -->
|
||||||
|
<g transform="translate(326,138)" fill="#FFFFFF" opacity="0.85">
|
||||||
|
<polygon points="0,-18 5,-5 18,0 5,5 0,18 -5,5 -18,0 -5,-5"/>
|
||||||
|
<circle cx="18" cy="-14" r="3.5"/>
|
||||||
|
<circle cx="14" cy="-22" r="2"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
3
python/_menus/index.json
Normal file
3
python/_menus/index.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
22
python/libraries/LICENSE
Normal file
22
python/libraries/LICENSE
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 QmiTek (https://qmikeji.cn)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
7
python/libraries/makergen/__init__.py
Normal file
7
python/libraries/makergen/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
"""适配makergen的Python SDK"""
|
||||||
|
|
||||||
|
__version__ = '0.1.0'
|
||||||
|
|
||||||
|
from makergen.core import Device
|
||||||
|
|
||||||
|
__all__ = ["Device"]
|
||||||
85
python/libraries/makergen/core.py
Normal file
85
python/libraries/makergen/core.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import json
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
from json import JSONDecodeError
|
||||||
|
from typing import Callable, Optional
|
||||||
|
|
||||||
|
import websocket
|
||||||
|
|
||||||
|
|
||||||
|
class ServerException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Device:
|
||||||
|
def __init__(self, device_id: str, device_key: str,
|
||||||
|
on_msg_callback: Optional[Callable[[str, any], None]] = lambda point, value : print(point, value),
|
||||||
|
on_error_callback: Optional[Callable[[Exception], None]] = lambda error: print(error),
|
||||||
|
base_url: str = "wss://api.makergen.cn"):
|
||||||
|
"""
|
||||||
|
创建一个设备对象。需要调用connect方法才能真正连接。
|
||||||
|
:param device_id: 设备ID,在控制台设备管理中显示
|
||||||
|
:param device_key: Device Key,在控制台设备管理中显示
|
||||||
|
:param on_msg_callback: 当收到消息后的回调。接收两个参数,第一个参数为控制点,字符串型;第二个参数为控制值,任意类型
|
||||||
|
:param on_error_callback: 当遇到错误时的回调。接收一个参数,为错误对象;若遇到服务器或页面发送的消息不合规,传入ServerException对象
|
||||||
|
:param base_url: makergen的基础URL,通常为wss://api.makergen.cn
|
||||||
|
"""
|
||||||
|
url = f"{base_url if base_url[-1] == '/' else base_url + "/"}ws/device/{device_id}?key={device_key}"
|
||||||
|
print(url)
|
||||||
|
self.ws = websocket.WebSocketApp(
|
||||||
|
url,
|
||||||
|
on_open=self._on_open,
|
||||||
|
on_message=self._on_message,
|
||||||
|
on_close=self._on_close,
|
||||||
|
on_error=self._on_error,
|
||||||
|
)
|
||||||
|
self.on_msg_callback = on_msg_callback if on_msg_callback is not None else lambda _, __ : None
|
||||||
|
self.on_error_callback = on_error_callback if on_error_callback is not None else lambda _ : None
|
||||||
|
self.is_connected = False
|
||||||
|
|
||||||
|
def _on_open(self, ws):
|
||||||
|
self.is_connected = True
|
||||||
|
def _on_close(self, ws, close_status_code, close_msg):
|
||||||
|
self.is_connected = False
|
||||||
|
|
||||||
|
def _on_message(self, ws, message):
|
||||||
|
try:
|
||||||
|
message_str = json.loads(message)["payload"]
|
||||||
|
self.on_msg_callback(message_str["point"], message_str["value"])
|
||||||
|
except (KeyError, JSONDecodeError):
|
||||||
|
self.on_error_callback(ServerException("Page or server sent an invalid message"))
|
||||||
|
except Exception as e:
|
||||||
|
self.on_error_callback(e)
|
||||||
|
|
||||||
|
def _on_error(self, ws, error):
|
||||||
|
self.on_error_callback(error)
|
||||||
|
|
||||||
|
def send_data(self, point: str, value: str):
|
||||||
|
self.ws.send(json.dumps({
|
||||||
|
"type": "data",
|
||||||
|
"payload": {
|
||||||
|
"point": point,
|
||||||
|
"value": value
|
||||||
|
},
|
||||||
|
"timestamp": int(time.time() * 1000)
|
||||||
|
}))
|
||||||
|
|
||||||
|
def connect(self, timeout: float = 5.0):
|
||||||
|
"""
|
||||||
|
在新线程中运行客户端循环(推荐)
|
||||||
|
"""
|
||||||
|
wst = threading.Thread(target=self.ws.run_forever)
|
||||||
|
wst.daemon = True
|
||||||
|
wst.start()
|
||||||
|
start_time = time.time()
|
||||||
|
while not self.is_connected:
|
||||||
|
if time.time() - start_time > timeout:
|
||||||
|
if not self.is_connected:
|
||||||
|
self.on_error_callback(TimeoutError(f"WebSocket connection timed out after {timeout} seconds"))
|
||||||
|
time.sleep(0.05)
|
||||||
|
|
||||||
|
def main_loop(self):
|
||||||
|
"""
|
||||||
|
在当前线程中运行客户端循环(不推荐)
|
||||||
|
"""
|
||||||
|
self.ws.run_forever()
|
||||||
1
python/libraries/requirements.txt
Normal file
1
python/libraries/requirements.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
websocket-client==1.9.0
|
||||||
46
python/main.ts
Normal file
46
python/main.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
//% color="#cf1256" iconWidth=50 iconHeight=40
|
||||||
|
namespace makergen{
|
||||||
|
|
||||||
|
//% block="连接设备 设备ID [ID] Device Key [KEY] 基础URL [BASEURL]" blockType="command"
|
||||||
|
//% ID.shadow="string"
|
||||||
|
//% KEY.shadow="string"
|
||||||
|
//% BASEURL.shadow="string"
|
||||||
|
//% BASEURL.defl="wss://api.makergen.cn"
|
||||||
|
export function connect(parameter: any, block: any) {
|
||||||
|
let id=parameter.ID.code;
|
||||||
|
let key=parameter.KEY.code;
|
||||||
|
let baseurl=parameter.BASEURL.code;
|
||||||
|
Generator.addImport(`\nimport makergen\nmakergen_device = makergen.Device(${id},${key},on_msg_callback=makergen_callback, base_url=${baseurl})\n`);
|
||||||
|
Generator.addCode(`makergen_device.connect()\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
//% block="向数据点 [POINT] 发送数据 [VALUE]" blockType="command"
|
||||||
|
//% POINT.shadow="string"
|
||||||
|
//% VALUE.shadow="normal"
|
||||||
|
export function send_msg(parameter: any, block: any) {
|
||||||
|
let point=parameter.POINT.code;
|
||||||
|
let value=parameter.VALUE.code;
|
||||||
|
Generator.addCode(`makergen_device.send_data(${point},str(${value}))\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
//% block="当接收到消息时" blockType="hat"
|
||||||
|
export function on_msg(msg: any) {
|
||||||
|
Generator.addEvent("makergen_callback", "makergen_callback", "point, value", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//% block="---"
|
||||||
|
export function separator() {}
|
||||||
|
|
||||||
|
//% block="控制点" blockType="reporter"
|
||||||
|
export function get_point(parameter: any, block: any) {
|
||||||
|
Generator.addCode(`point`);
|
||||||
|
}
|
||||||
|
|
||||||
|
//% block="控制值" blockType="reporter"
|
||||||
|
export function get_value(parameter: any, block: any) {
|
||||||
|
Generator.addCode(`value`);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user