admin管理员组文章数量:1794759
从零开始:UlanziDeck插件开发之旅
UlanziDeck介绍
在数字时代,个性化和效率是我们追求的核心。Ulanzi Stream Deck作为一款可视化键盘,拥有强大的桌面控制设备,通过其插件系统,为开发者和爱好者提供了无限的创造空间。本文将带你从零开始,探索Ulanzi Deck插件的开发过程,让你的桌面控制体验更加个性化和高效。
环境准备
在开始之前,请确保你已经安装了以下工具和环境:
- Node.js版本20或更高。
- Ulanzi Deck 软件
- 集成开发环境,VS Code、WebStorm等任意。
- Ulanzi Deck Plugin SDK
插件展示
下载完UlanziDeck软件后,打开右上角应用市场,下载模拟时钟,让我们看看模拟时钟的效果
接下来我们来探索一下插件实现的过程
插件实现
1.插件文件夹
在该位置下我们看到了模拟时钟的插件文件夹,我们来看下文件夹的结构
代码语言:txt复制com.ulanzi.analogclock.ulanziPlugin
├── assets //主要用于存放上位机icon的展示和action状态的切换
│ └── icons
│ └── icon.png
├── libs //插件通用库,此处不做具体介绍,可前往Ulanzi Deck Plugin SDK查看。
│
├── plugin //js主要功能模块,包括action的处理
│ ├── actions //处理具体action逻辑
│ ├── app.html //主服务html,作为入口
│ └── app.js //主服务js
├── property-inspector // 配置项html和form表单的读写
│ └── clock //action的名称
│ ├── inspector.html //配置项html
│ └── inspector.js //配置项js,用于做socket连接和form表单的处理
├── manifest.json //具体配置
├── zh_CN.json //中文翻译文件
├── en.json //英文翻译文件
2.Manifest.json 插件配置解读
首先让我们来看下插件配置文件
代码语言:json复制manifest.json
{
"Version": "1.0.4",
"Author": "Ulanzi",
"Name": "Analog Clock",
"Description": "Always be on time.",
"Icon": "assets/icons/icon.png",
"Category": "Analog Clock",
"CategoryIcon": "assets/icons/categoryIcon.png",
"CodePath": "plugin/app.html",
"Type": "JavaScript",
"SupportedInMultiActions": false,
"PrivateAPI": true,
"UUID": "com.ulanzi.ulanzideck.analogclock",
"Actions": [
{
"Name": "clock",
"Icon": "assets/icons/actionIcon.png",
"PropertyInspectorPath": "property-inspector/clock/inspector.html",
"state": 0,
"States": [
{
"Name": "clock",
"Image": "assets/icons/icon.png"
}
],
"Tooltip": "Show a nice analog clock",
"UUID": "com.ulanzi.ulanzideck.analogclock.clock",
"SupportedInMultiActions": false
}
],
"OS": [
{
"Platform": "mac",
"MinimumVersion": "10.11"
},
{
"Platform": "windows",
"MinimumVersion": "10"
}
],
"Software": {
"MinimumVersion": "6.1"
}
}
Icon/Name:
在软件左侧列表显示的插件图标和插件名称
Category/CategoryIcon/Author/Description/Version
在设置->插件中右侧区域显示的名称、图标、作者、描述、版本信息
CodePath
插件的主程序,可以为".html"和".js",".html"时使用QWebEngineView加载,".js时"使用Node(版本为20.12.2)加载
UUID
插件的主程序的唯一标识
Actions -> Name/Icon
插件展开的配置项对应的名称和图标
Actions -> PropertyInspectorPath
插件对应的参数配置html,会由桌面软件加载在下方的红色区域
Actions -> UUID
插件的某个配置项的唯一标识
Actions -> SupportedInMultiActions
插件配置项是否支持多项操作,true代表点击进入多项操作后,支持拖入该插件配置进入多项操作
Actions -> States/state
两个按键位置的默认图标,支持复数个图标,state表示图标数组中的坐标位置,可以通过协议切换坐标位置来切换图标。因为模拟时钟会通过绘制表图更新按键位置,所以不会见到上方箭头按键的默认图标
好了,以上是比较重要的Manifest属性,下面我们介绍一下通用的约定,方便我们快速上手插件开发
通用约定
为了统一管理,我们的插件包的名称为 com.ulanzi.插件名.ulanziPlugin
为了SDK的正常使用,主服务连接的uuid我们约定长度是4。例:com.ulanzi.ulanzideck.插件名
配置项连接的uuid要大于4用于区分。例:com.ulanzi.ulanzideck.插件名.插件action
为了UI字体的统一,我们已经在udpi.css设置了开源字体思源黑体(Source Han Sans),在app.html也同样需要引用字体库。请大家在绘制icon时,统一使用'Source Han Sans'。
上位机的背景颜色为 '#282828',通用css(udpi.css)已经设定了'--udpi-bgcolor: #282828;'。若要自定义action的背景颜色应与上位机背景色相同,避免插件背景颜色过于突兀。
通过以上介绍,我们应该了解了插件Manifest的配置,并提及了
插件主程序(CodePath)
Action和Action参数配置Html(PropertyInspectorPath)两个概念,接下来给大家更详细地介绍一下这两个部分。
以下我们会将插件主程序称为app 或 app.js/html, 将配置项称为action
3.插件主程序、Action/PropertyInspector加载
插件主程序
刚刚我们提到了桌面软件会将插件Manifest中的CodePath,作为插件的主程序加载。那么主程序需要通过ws client,与上位软件建立常态通信,主程序的生命周期为:桌面软件的启动至退出,或是插件的安装至卸载。我们来看下主程序的具体加载过程:
使用node加载 app.js 时,会传递Websocket服务端地址及端口参数:
代码语言:powershell复制 node.exe "app.js" "127.0.0.1" "3906" "en-US"
加载app.html时,Url添加键值对参数:<"address", "127.0.0.1">,<"port","3906">,<"language","en-US">, <"uuid", "manifest中UUID">, <"actionId", "">, <"key", "0_0">
代码语言:javascript代码运行次数:0运行复制app.html?address=127.0.0.1 //ws server链接
&port=3906 //ws server端口
&language=en-US
&uuid="com.xxx.xxx" //主服务的UUID
Action/PropertyInspector
当拖拽设置时钟至按键区域后,下方显示的是Manifest中的Actions -> PropertyInspectorPath,PropertyInspector的生命周期是在下面区域显示至从下方区域消失
具体加载的方式如上
代码语言:javascript代码运行次数:0运行复制PropertyInspecto.html?address=127.0.0.1 //ws server链接
&port=3906 //ws server端口
&language=en-US
&uuid="com.xxx.xxx" //Action的UUID
&actionId="" //每个Action的唯一ID,区分不同页的Action
&key="0_0" //Action在当前页的坐标,在5*3方格区域里13个可设置区域,左上是0_0,任意位置为“行_列”
4.服务端、主程序、Action之间的通讯
服务端事件
以UlanziDeck Plugin SDK中为例,可以看到,在实现插件 app.html/js 和 propertyInspector.html时,应当首先与服务端建立连接: $UD.connect(UUID) ,随后通过监听服务端传来的事件,完成插件的功能。
插件端请求
接下来是可以向服务端发起的请求
通过上面两部分的消息接收/发送,我们应该可以理解
服务端 <=> app.html/js
服务端 <=> propertyInspector.html
的通讯过程,那么随之而来的问题是,app.html/js 是怎么与 propertyInspector.html 进行通讯的
以模拟时钟选中数字为例,来介绍下app与propertyInspector的通讯
服务端的消息转发
我们来看下propertyInspector的实现,通过监听html元素的表单变化,当“数字”选项发生变化时,会将参数更新给服务端:sendParamFromPlugin
服务端在接收到Action发来的参数更新时,会将带参数消息直接转发给Action的主服务;
反之同理,当主服务发起参数更新消息时,也会将消息转发给参数当前显示的propertyInspector.html
代码语言:javascript代码运行次数:0运行复制propertyInspector.js
$UD.connect('com.ulanzi.ulanzideck.analogclock.clock')
$UD.onConnected(conn => {
//获取表单
form = document.querySelector('#property-inspector');
//渲染option
const oClockSelector = document.querySelector(".clockSelector");
Object.keys(clockfaces).map(e => {
let option = document.createElement('option');
option.setAttribute('value', e);
option.setAttribute('label', clockfaces[e].name);
option.setAttribute('data-localize', clockfaces[e].name);
oClockSelector.appendChild(option);
});
//连接上socket,显示配置项
const el = document.querySelector('.udpi-wrapper');
el.classList.remove('hidden');
//监听表单变化,发送参数到上位机
form.addEventListener(
'input',
Utils.debounce(() => {
const value = Utils.getFormValue(form);
ACTION_SETTING = value
if(!ACTION_SETTING.clock_type) ACTION_SETTING.clock_type = 'analog'
$UD.sendParamFromPlugin(ACTION_SETTING); //发起参数更新
})
);
});
在app端接收来自action的参数更新消息,并刷新绘制
代码语言:javascript代码运行次数:0运行复制app.js
//监听插件功能配置信息变化
$UD.onParamFromPlugin(jsn =>{
onSetSettings(jsn) //接收到propertyInspector发起的参数更新消息
})
//更新参数
function onSetSettings(jsn){
const settings = jsn.param || {}
const context = jsn.context //context中含有action的UUID信息
const clock = ACTION_CACHES[context]; //找到具体按键位置action的实例
if(!settings || !clock) return;
if(settings.hasOwnProperty('clock_index')) {
const clockIdx = Number(settings.clock_index);
clock.setClockFaceNum(clockIdx);
ACTION_CACHES[context] = clock;
}
if(settings.hasOwnProperty('clock_type')) {
clock.setClockType(settings.clock_type); //更新“数字”参数到实例
}
}
5.Action的active状态
在服务端事件中我们可以看到onSetActive事件,因为按键区域存在着 配置文件 和 页码 的切换,在切换过程中,非当前页的action会被设置为非active状态,例如切换到第二页时,第一页的时钟会接收到非active的状态通知,这时我们需要去暂停资源,停止定时请求等节省消耗,并等待active状态的通知重新恢复。
//插件功能活跃状态设置
$UD.onSetActive(jsn =>{
const context = jsn.context
const instance = ACTION_CACHES[context];
if (instance) {
instance.setActive(jsn.active)
}
})
6.Action的移除
Action的移除伴随着服务端onClear的事件通知,移除会有三种触发方式
1)按键的移除 2)页面的删除 3)配置的删除
总结:
以上,我们介绍了从插件文件的创建,插件加载,以及服务端和插件的通讯流程,有任何问题和错误,请及时联系我嘿嘿。代码的详细实现和更详细的通讯接口,可以在Ulanzi Deck Plugin SDK上查看,下篇文章会给大家展示插件的调试流程,和node方式的插件解读,敬请期待~
本文标签: 从零开始UlanziDeck插件开发之旅
版权声明:本文标题:从零开始:UlanziDeck插件开发之旅 内容由林淑君副主任自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.xiehuijuan.com/baike/1754636402a1704622.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论