Reading view

There are new articles available, click to refresh the page.

我做了一个可以锻炼视力的 App

没错,我做了一个新 App,可以帮助你放松眼睛、缓解屏幕疲劳,甚至改善视力。

说实话,要不是因为这次疫情,估计打死我也想不到会做个这样的 App。

本来我视力一直都挺好的,也许是因为我常年在外旅行摄影,即使在家也保证每天都有户外运动,所以每天平均下来的屏幕时间并不多。没想到疫情一来,不仅旅行计划全部取消,整个生活节律都被打乱了。居家隔离期间,屏幕看得越来越多,没几天就明显感觉到视力有下降的趋势。

有天和一位眼科医生朋友聊起这事,他建议我每天做几次眼保健操,坚持一段时间会有好转。我做了几天之后,确实有些效果,不过也遇到了三个问题:

  1. 总是想不起来要做;
  2. 经常遗漏具体步骤;
  3. 无法确认动作是否到位。

正常人遇到这种情况设个备忘录就行,但我属于爱瞎折腾的人,加上宅在家也是闲着,不如做个 App 来督促一下自己吧。

@dandyweng 发布于 2020 年 2 月 20 日的微博:这两天受到鼓舞了,脑子里又蹦出来一个特别牛逼的 idea,看看几天能做出来,反正 flag 我就先立在这了[doge]

其实我经常做一些自娱自乐的小应用,大部分都没有公开发布,因为我觉得并没有什么实用价值,比如用手机 3D Touch 屏幕称重的电子秤,没想到却在一夜之间爆红网络,这也激励了我把这次的想法落地,做成可以上架的 App。

这三个问题中,前两个相对好解决,第三个就要运用一些黑科技了。正好两年前我就研究过用 iPhone X 的 TrueDepth 相机(就是那个用来 Face ID 刷脸解锁的传感器)结合 ARKit 框架进行眼球追踪方法,这次正好可以派上用场。

15 天之后,Eyeye 诞生了。

Eyeye 视力锻炼:运用 AR 眼球追踪技术,指导你的日常视力锻炼。眼保健操新体验:App 会指导你做出每一步动作,并通过 TrueDepth 相机系统捕捉并分析眼球的运动轨迹和眼周肌肉的微弱变化、眼睛与屏幕之间的距离等全方位数据,确保你准确到位地完成每一个动作;缓解视力疲劳:帮助你缓解由于长时间盯着屏幕、书本等导致的眼睛疲劳,非常适合经常高负荷用眼的人群;养成健康护眼习惯:内置每日推送提醒,促使你建立定期活动眼睛的好习惯,减缓视力下降,长期坚持甚至可以在一定程度上改善视力。前往 App Store 搜索 Eyeye 免费体验。

Eyeye 目前有两种模式,完全免费的「轻松一下」提供了一套基础动作,可以有效缓解长时间高负荷用眼导致的眼疲劳;如果你想更进一步,只需要 18 元就可以解锁「进阶锻炼」获取更高效、全方位的锻炼方式,同时也可以支持一下我后续的开发维护工作。

具体锻炼步骤都是我的那位眼科医生朋友提供的,按他的说法,只要每天坚持锻炼至少三次,大部分人(包括成年人)的视力都是可以慢慢改善的。至于实际效果如何,从我这段时间的个人体验来看确实很不错,感觉自己的视力基本恢复到了往年的正常水平,大家也可以体验一下,然后与我分享你的实际感受。

如果你的设备不支持眼球追踪功能,依然可以在手动模式下使用:只需按照指示做出所需的动作,然后轻触屏幕的任意位置即可继续下一个动作。App 虽然不能判断你的动作是否到位,但依然能够起到提醒与引导作用,可以解决我所遇到的那三个问题的前两个。

Eyeye 刚刚上线,目前的功能还比较简单,我会在接下来的时间里慢慢完善它。如果你有什么好的想法,可以在评论区聊聊。

如果你喜欢,别忘了在 App Store 给写个五星好评,非常感谢!

点此前往 App Store 下载

前往 App 官网了解更多

个人网站第七次改版

从 2010 年起,我每年都会重新设计与开发一次自己的个人网站,每次改版都会整理自己在过去一年里所学到的东西并与大家分享。直到 2015 年,几乎是在准备第六次改版的同时,我忽然有了二维信息流、模块化内容和私密社交网络的想法,于是开始构思如何将这些想法转化为一个产品、设计基本界面、开发交互原型,Vary 就此诞生。不过时间精力受此影响,第六版网站设计非常简单,开发过程也很仓促,没想到却是服役最久(1297 天)、最多人看过(99.6 万)的版本。

2016 年开始,我几乎所有的时间精力都用在了 Vary 的开发上,无暇顾及个人网站,持续到 2017 年底才暂告一段落,紧接着又开始为我的个人摄影作品展示平台 Camarts 重新设计和开发全新版本,直到今年首个测试版 PWA 上线之后,我才有心思回顾这些年的成长历程,开始第七次改版。

迟到了三年的第七版,终于在今天正式上线。

新版运用了时下流行的超大字号、渐变文字、弥漫投影、错位堆叠等设计元素,继续保持极简风格;技术方面大量应用了 CSS Grid 和 CSS 变量等新规范,也首次尝试使用 GSAP 开发基于页面滚动的渐进式视差动画效果;这也是我首个贯彻 Progressive Enhancement 理念——即内容优先,将页面布局和基本浏览体验与 JS 解耦,无框架或构建工具等硬性依赖,使用纯手写的静态 HTML 与原生 JS 使得在 iPad 上开发成为可能……更硬核的东西就暂且省略,如果大家有兴趣我再另起一篇技术文章详述。

好了,那就不多说,点击这个链接即可体验新版:https://www.dandyweng.com

One More Thing

嘿嘿,再多说几句。其实在开发新版网站期间,我又做了一个 app,叫做 Markly。

如果你像我一样,无论去哪里都用手机拍很多照片,那么你一定会喜欢 Markly——将你拍摄照片的地点展现在精美地图上的数据可视化应用。

Markly 会自动扫描你的相册并统计拍照地点,让你轻松用全新的视角看看你的生活:在哪儿生活过?平时都喜欢去哪里?去过什么地方旅行?一目了然。还有更多功能即将推出,比如显示你的旅行足迹、统计你去过多少个国家和城市等等。

相册和位置信息含有个人隐私数据,承载了生活的方方面面,因此 Markly 十分注重数据的私密性,仅读取每张照片附带的 GPS 元数据,不读取影像内容,并且所有数据都在本机处理。因此,在征得你的明确许可之前:

* 不会上传任何从你相册里获取到的数据;
* 不会以任何方式改动你的相册或者照片。

Markly 目前还在初期开发阶段,预计在下个月上线,小范围的 TestFlight 内测会在近日开放。如果你有兴趣参与测试,可以识别下方的二维码关注我的微信公众号,然后发送你的邮箱来申请,名额有限,先到先得。

Unity + AR 体验记

在 iOS 11 发布时,作为其重磅功能之一的 ARKit 就引起了我强烈的兴趣。在最初的 beta 版本放出后,我就第一时间下载更新,并开始研究它的应用方法。

那段时间我正好也对著名的游戏引擎 Unity 很感兴趣,不满足于制作小游戏的我便开始琢磨如何将 iOS 的 ARKit 和 Unity 结合在一起来实现 AR(即 Augmented Reality,增强现实)效果。这时我发现了 Unity 的 ARKit 插件,它让开发者可以很方便地调用相关 SDK。借助小时候掌握的建模技能和这么些年来的编程经验,轻松实现了这样的 AR 效果:

由于不久后我的兴趣点又被吸引到了物联网智能家居上,那一次 AR 探索也就浅尝即止了。最近几天,来自瑞典的一位设计师 Peder Norrby 实现的幻象应用 TheParallaxView 走红网络,他除了用到上述的 Unity ARKit 插件外,还结合了 iPhone X 的 TrueDepth 传感器进行面部追踪,根据眼睛与屏幕的相对位置和距离来调整 Unity 场景的相机,由此实现虚拟的 3D 立体视觉效果。

完整视频:秒拍 | YouTube

这个应用最初只是在国外的开发者圈子里流传,随后经过微博科技圈几位博主的转发,在国内也成为了热门,许多人都迫不及待地想亲身体验它的效果。我此前就已经在 Twitter 上关注了作者,这次也是近水楼台先得月,经过交流之后,借助他提供的设计思路和源代码,我也在自己的 iPhone X 上实现了这个效果。

虽然原作者正在尝试将这个应用上架 App Store,却在审核环节遇到了些麻烦,目前不知何时或能否成功上架。我本想写一篇教程告诉想马上体验的朋友们我是如何自己实现的,转念一想这需要安装 Unity 和 Xcode 这两个大型软件及各种插件,还得自行配置开发环境和编译源代码,其中环节和变数太多,对于没有相关经验的朋友们可能并没有太大帮助。所以我打算将自己编译的版本通过 Apple 官方的软件测试平台 Testflight 与大家分享,如果你有兴趣可以在本文的评论处报名并留下自己的邮箱,若报名的人数足够多,我会在征得原作者的首肯之后申请 Testflight 并向大家发送邀请和安装方法。

后续我也会找时间来进一步研究 AR 方面的技术,并尝试将 Unity + ARKit 与 Cinema 4D 和 Asset Forge 等建模工具结合来实现更多酷炫好玩的东西,有新发现时我会第一时间在这里与大家分享。

💾

💾

微信小程序初体验

在一年多前微信刚刚推出小程序的时候我是很感兴趣的,早早下载了它的开发工具来研究,可是后来发现微信却不允许个人开发者自己发布小程序,我的开发计划便不了了之了。

直到今年,微信总算是对个人开发者开放了小程序的部分功能,虽然有着诸多限制,但总算允许以个人名义发布了,于是我开始给自己开发一个小程序版的博客。

目前这个小程序已经发布,用微信扫一扫上图里圆形的小程序码即可在小程序里查看这篇文章。如果你正在手机上查看,可以将上图保存到相册,然后在微信的扫描二维码处点击从相册选择图片。

小程序主要是用 JS 配合微信自己的 WXML 和 WXSS(本质上就是 HTML 和 CSS)来开发,对于有着 Web 前端开发经验的我来说很容易上手。不同之处在于这里不能直接操作 DOM,也不能用 jQuery 之类熟悉的框架,而是用上了类似 React 和 Vue 的 MVVM 架构模式。最近几年 Web 前端领域日新月异,我却将重点转移到了 iOS 端开发,恰好错过了使用这类新架构的机会,这次借机学习一下新东西,就当是重新入门前端开发了,嘿嘿。

我的博客是基于 WordPress 的,只要对自带的 RESTful API 稍加调整即可使用,所以服务器端基本没有多少开发工作。小程序客户端方面,需要实现的是个人简介、文章列表、正文、评论和留言板等界面。

文章列表页

先从用来展示最新文章列表的首页开始,这个页面比较简单,从服务器获取 JSON 格式的文章列表数据,然后遍历显示标题、摘要和图片即可,再给每个条目都绑定点击事件,点击之后携带文章 ID 跳转到正文页。小程序页面间是通过类似于 HTTP GET 的方式传值的,需要手动把参数拼接到 URL,一点也不优雅。

正文页

正文页传输过来参数会自动被小程序封装到一个 options 对象里,从这里读取到文章 ID 之后就可以将它发送到服务器接口请求文章数据了。不过 WordPress 的文章正文是 HTML 格式的,而微信小程序并不支持原生 HTML 标签,因此无法直接显示内容。还好,经过一番搜寻,找到了 wxParse 这个可以将简单的 HTML 转换为 WXML 的框架,它会通过 JS 匹配所有的 HTML 节点,然后将它们转换成 JS 对象,再用一个非常复杂的 template 模板把内容渲染出来。虽然绕了一个大弯子,不过正文页总算是实现了。

评论页 / 留言板

这是相对来说逻辑最复杂、开发工作量最大的一个界面。嗯,其实逻辑本身并没有多复杂,工作量也不应该这么大,毕竟我的博客网页版就是用 JS 异步获取评论的,理论上逻辑都差不多,搞不好还能复用一部分代码……

开发微信小程序的时候,写着熟悉的 JS 和 CSS,用着 Chrome 的调试器(虽然只是个阉割版),恍惚间总会觉得自己正在轻松自如地做网页,然后时不时就会被现实啪啪两下耳光扇清醒。

刚开始制作评论页面时还挺顺利的,获取从上一个页面传进来的文章 ID,然后从服务器端接口获取文章列表,页面滚动到底部时自动载入更多等等都很容易实现。样式方面确实复用了网页版的许多 CSS 样式,所以开发起来还算轻松,全然不知一只脚已经踩进了大坑。

当我准备处理回复评论显示的时候才发现,微信小程序竟然不能递归嵌套 DOM 元素!WordPress 的评论嵌套是子评论中保存父评论的 ID 字段来实现的,在绝大多数编程语言中,只需写个简单的递归即可实现类似下面这样的树状结构:

这样即可将相关的评论都显示在一起,方便“盖楼”回复。可是 WXML 的 DOM 元素必须静态写好,除了 for 循环之外似乎并不能在 runtime 动态添加节点,然而 for 循环只能用来生成单层结构,不能嵌套;我试过用 template 嵌套自身来实现递归,失败;用 template A 来嵌套 template B 可行,但 B 却不能再套回 A,失败;用 for 单层渲染,然后用 @ 的方式实现类似于微博的评论显示方式,体验太差,放弃。那就先全部渲染出来,再用 JS 把 DOM 挪到对应的父元素里?抱歉,不可以直接操作 DOM。

上网搜了一圈,吐槽这一点的开发者不少,比如 wxParse 的作者在一篇文章中提到的:

无法循环使用模版下,如何处理多级 HTML 嵌套解析?既然不能够实现完美的代码较少的方式,那我们就采用一种笨办法,手动循环出 N 个模版,因为在实际场景中,尤其是在文章的 HTML 中不会出现超过 10 级的嵌套,那么这样我们可以手动写 10 个模版,进行循环调用。

难道真的得用这么笨的方法来写?何况文章内的 HTML 嵌套或许不会超过十级,但评论盖楼就是另一回事了。不甘心,继续查找解决方案。翻遍了官方文档,看完了为数不多的开源项目,浪费了若干小时的宝贵时间,却并没有找到更好的方法。有一个同样基于 WordPress 的开源微信小程序也是用手动写死五层嵌套的方式来实现的:

这对于有代码整洁强迫症的我来说简直是无法忍受的。然而在这个问题上耗费的时间精力已经超过开发整个微信小程序(包括服务器 API 调整)的总和,我不得不妥协,在代码整洁和功能完整之间选择了一个平衡点:手动写了一层嵌套。所以现在只能看到评论的一级回复,给这个回复的再次回复在小程序里是显示不出来的。思路虽然简化了,但实际开发过程中还是有很多需要额外处理的地方,细节我就略过不表了。

相对而言,发布 / 回复评论的页面就简单多了,界面就是一个评论表单,只需获取评论者的微信昵称和头像 URL,跟着表单数据一起提交给服务器即可。本想将评论者的微信头像 URL 作为一个 commentmeta 存到 WordPress 的数据库里,没想到才出狼窝又入虎穴,又踩到了 WP REST API 的一个大坑:自带的保存评论接口不能提交 commentmeta 字段。关键是不能提交你明说也行啊,我想别的办法就好,偏偏官方文档又语焉不详,让我查了一大圈,最后一气之下自己重写了整个接口。这个经历让我更坚定了 Camarts 改版时要自己写 CMS 的想法。

再加上点赞等功能之后,评论功能总算是完成了,留言板复用了评论页面,只是稍微调整了一下界面细节使之更适合显示较长的内容。麻烦之处在于留言板的入口,我的设计是在界面底部的 Tab Bar 导航栏上,微信小程序提供了这个组件,目测是原生实现的,切换起来挺流畅;不足之处在于样式太死板,可供自定义的选项不够丰富,调整了半天依然实现不了心目中的设计,只好自己用 WXML 和 WXSS 实现了,虽然麻烦些,但可以完全自己设计,在 iOS 端还能实现半透明模糊效果,看起来漂亮多了。

个人简介页

Tab Bar 上的另一个页面是我的个人简介页。原本我打算将我个人主页(dandyweng.com)上的内容全部移植过来,但前面的折腾已经耗费了太多时间精力,我也实在是没心思继续了,于是只写了几段文字,加上进入我个人主页的按钮,草草了事。由于微信小程序不能直接跳转到网页,所以点击按钮之后只能将网址复制到手机剪贴板,还需要到浏览器地址栏粘贴才能访问。

我对文章正文内的站外链接也做了类似处理,再完善一些界面细节,本次的微信小程序就基本完成了。

小结

通过这次的开发体验,实话说,我对微信小程序总体上是偏向失望的。

诚然,小程序在网页和原生应用的结合方面做得确实不错,我开发 Vary 时也遇到过网页与原生结合的难题,深知其中不易。比如当跳转页面时,可以明显看出是通过原生方式实现的,体验远超网页的链接或 AJAX 跳转;数据和 UI 分别加载的方式大幅提高了加载速度,达到了与原生应用非常接近的用户体验,同时微信扫个二维码或者在聊天窗口里直接点击卡片就能进入小程序,免去了原生应用需要下载的麻烦;更重要的是能够一次开发即可同时投放 iOS 和 Android,虽然必须在微信里运行,但以微信的体量而言,这几乎不是问题。

那为何失望?归结起来就两个字:限制。

微信小程序本身作为一个产品,在规划时需要一定限制,这可以理解;技术层面上的复杂度太高,为了保证可用性做出限制,我也可以理解。不能接受的是人为的、毫无说服力的限制。尤其是对于个人开发者而言,小程序看似有很多功能,但实际开发时往往都是这个不能用、那个不让用。这点我在几年前开发微信公众号机器人的时候就有过类似体验,看来是微信乃至腾讯一脉相承的做法。

我承认要运营这样一个互联网产品不易,个人开发者确实更难监管,但如此草率地“一刀切”歧视所有个人开发者正是对自己的管理能力缺乏信心的表现。不恰当地反观 Apple,在开发和上架一个 iOS app 的过程中就完全感受不到个人与企业名义有任何区别。当然,除去公司之别亦有国情之别,其中微妙不言自喻。

限制二字更多体现在审核环节。对微信小程序审核之严格我早有耳闻,为了确保不触碰任何红线,我在开发前就仔细研读了官网的规范,但在开发完成提交审核时还是以各种含混不清的理由被拒绝了,比如说我的博客含有“文娱资讯”内容,必须要相关资质的企业才能发布等等,如何定义“文娱资讯”?具体哪篇文章违规了?无人告知,申诉无门。上网查了一圈才知道,大家都称之为“薛定谔的审核”,基本上是碰运气,一次过不了随便改点东西再试一次可能就过了。

这时我才知道,原来那个“规范”只是个摆设,能不能发布是审核员说了算的,然而审核员是很不靠谱的,他主观认为你行就行,说你不行就不行。而且我觉得审核员是倾向于拒绝的,因为他要是审核通过之后出了什么问题估计就得自己背锅了,因此宁可错杀一千也不放过一个。

这给了我极大的不安全感,因为自己辛辛苦苦学习、设计、开发和调试的心血之作很有可能因为某个不靠谱的审核员的一念之差而全部付诸东流。这种不安全感让我不敢、也不愿再为它付出更多的时间精力。

最终我的小程序虽然侥幸通过审核,但可能不知哪一天就会触碰到某条看不见、摸不着、说不清、道不明的规定而被强行下架。所以大家趁着现在还能用,赶紧体验吧。

HomeKit 智能家居深入体验

作为一个从小就爱捣腾数码产品的人,我对智能家居自然有着浓厚的兴趣。最近家里重新装修,便换上了批智能家居设备,我也借此机会对这个新兴领域深入研究了一番。

我觉得智能家居最重要的一点,就是用起来要比传统的控制方式更方便。这听起来像是废话,可实现起来却并不太容易,毕竟绝大多数所谓“智能家居”配套的 App 都非常非常非常难用,比如下图这种画风……即便难得遇到些好用的,想控制不同厂商的设备还得打开不同的 App 也是挺傻的一件事。

还好 Apple 去年发布了 HomeKit 平台,通过这个平台可以将不同厂商的智能家居设备聚合在一起显示和控制,并且深度集成到了 iOS 系统里,在锁屏状态下都可以直接控制所有智能家居设备,完全不需要打开厂商提供的 App;甚至可以不碰手机,直接喊 Siri 帮你控制即可,非常方便(逼格也不知要高到哪里去了);配合 Apple TV 或 iPad 作为控制中枢还能实现人不在家时的远程遥控和自动化控制等等。

然而 HomeKit 虽然已经发布了一整年,硬件的支持情况却并不理想,在国内更是没有得到足够的重视,很多标榜智能家居的厂商甚至不知其为何物。无奈之余,我也本着极客精神,开始研究自行接入的方法。

梳理一下我家的电器设备,大致可分为以下三类:

  1. 原生支持 HomeKit 的设备,如 Philips Hue 系列灯具等;
  2. 完全不支持通过网络控制的传统电器,比如空调、新风系统和电动窗帘;
  3. 可以通过网络控制,却未提供原生 HomeKit 支持的设备,比如米家系列、Sonoff 开关和 Dyson 风扇。

下面我就分享一下我的研究(折腾)经历,看看我是如何把它们统统加进 HomeKit 里的。需要注意的一点是:本文仅为个人经验分享,并不是教程,所以一些细节问题我就不展开了。相信网上已经有了不少相关教程,若有需要可自行搜索,也可以在这里评论与我交流。

原生支持的设备

这里是指生产商官方提供 HomeKit 支持的设备,普通用户就能开箱即用。然而所支持的设备并不多,我手头只有 Philips Hue 系列灯具和 OPSO 的智能插座等。

添加 Hue 灯具的方法非常简单:把 Hue 网关用网线接到路由器上,在官方 app 里根据提示搜索并绑定网关,然后添加灯具即可,完成后扫描一下网关上的序列号,所有灯具都会自动同步到 HomeKit 里。

不得不说,Hue app 的界面设计和用户体验是我所用过的智能家居类应用里最好的。不过把灯都加进 HomeKit 之后,这个 app 也就没太大用处了。

接下来是 OPSO 的智能插座,这是 Apple Store 官网上的唯一一个支持 HomeKit 的国产设备,产品包装很有苹果风,做工也不错,只需在 iOS 自带的 Home app 里扫描设备上的序列号即可使用,非常方便。不足之处是有点不太稳定,使用几天后出现掉线的情况,需要重置设备后重新绑定才能用,希望可以尽快通过固件更新解决。

添加之后默认显示的是插座图标,不过可以手动将它修改成一个灯泡

这就是目前作为普通用户所能体验到的 HomeKit,但我并不会止步于此,下一步将会用些非常规的技术手段把那些并未提供原生支持的设备也加进来。

要实现上述功能,需要借助一个名为 Home Assistant 开源平台(下面简称 HA),它运行在基于 Linux 的系统上,可通过 Web App 和 API 来访问,接入不同的模块即可控制局域网内的各种智能家居设备。

搭建 HomeAssistant

首先需要一个基于 Linux 系统的设备来运行 HA,由于它需要 24 小时不间断运行,所以我选择了超低功耗的第三代 Raspberry Pi(树莓派)。它虽然才一个巴掌大,本质上却是个功能完善的、基于 ARM 架构的电脑,USB、HDMI 和网线等接口一应俱全。

考虑到这台树莓派主要用于运行 HA,我选用了 HA 官方提供的 hassbian 系统。从 HA 官网下载 hassbian 镜像,在电脑上用 Etcher 写入到空白 TF 卡中即可。

安装完成后,将 TF 卡、电源线和网线都插到树莓派上就可以开机了。首次启动时 hassbian 会自动安装和配置所需环境,等待一两分钟后,在局域网内的其他设备上访问 http://hassbian.local:8123 就可以进入 HA 的 Web App 主界面。

这里可以看到 HA 已经自动识别了 Philips Hue 网关和局域网内的两台 Apple TV。这里的 Hue 是可以一键配置的:点击 Configure 再按下 Hue 网关上的实体按钮即可将所有相关灯具都自动添加进来。虽然 Hue 灯具本身已经可以用 HomeKit 控制了,但在 HA 中可以实现更多有趣的玩法,比如自动循环变色等等。

接入传统遥控设备

下一步开始接入那些不能通过网络控制的传统电器,这里需要用到另一个硬件:可以联网的万能遥控器。经过一番比较,我选择了对 HA 支持较好的 BroadLink,它可以发射用于控制空调等电器的红外射频信号和窗帘电机的 FM 433Mhz 信号。

单从硬件角度来看 BroadLink 非常不错,但是它配套的 iOS app 实在是……太!差!了!还好它对 HA 有着良好的支持,所以用这个 app 把它连上 Wi-Fi 之后就可以果断删掉了。

按照官方文档中的说明将 BroadLink 的 IP 和 MAC 地址等信息填入 HA 的配置文件后,在服务列表里就可以看到它了。通过 HA 调用 learn_command 指令,BroadLink 将会进入学习状态。此时用空调遥控器对着它按下开机键,HA 首页便会显示遥控空调开机的指令代码。

接下来要做的就是将这个指令代码按照官方文档的格式复制到 HA 的配置文件里,并给它设置一个名称。用同样的方法配置关机键后,重启 HA 即可在首页看到空调的开关。在 Web App 上点击开关,HA 便会读取这个指令代码并通知 BroadLink 对着空调发送与普通遥控器相同的控制信号,达到通过网络控制电器的目的。

窗帘电机、风扇和空气净化器等其他有遥控器的电器设备也用同样的方法设置之后就都可以通过 HA 来控制了,下一步是将 HA 接入到 HomeKit 上。

连接 HA 与 HomeKit

这里需要用到一个叫做 Homebridge 的工具,它可以在树莓派上调用 iOS 的 HomeKit API 虚拟出一个网关来,然后再用它的 HA 插件将之前添加过的电器都同步到 Homebridge 即可实现操作。安装过程非常简单,参考这几篇官方教程即可:

Homebridge 的 Github 主页
https://github.com/nfarina/homebridge

在树莓派上安装
https://github.com/nfarina/homebridge/wiki/Running-HomeBridge-on-a-Raspberry-Pi

与 HA 建立连接
https://github.com/home-assistant/homebridge-homeassistant

安装完成之后就可以在 iOS 设备上的 Home app 里添加 Homebridge 了,这里与原生支持 HomeKit 的硬件相同的添加步骤完全相同,只不过设备名称和设备序列号都可以在配置文件里自定义,能把自己的名字写到“厂商名”处的感觉还是挺好玩的。

至此,HA 上的所有电器设备就都可以直接通过 HomeKit 来控制了。下一步接入能通过网络控制但未提供原生 HomeKit 支持的电器,比如小米家居(米家)系列。

接入无原生支持的设备

我家的绝大多数单联墙壁开关都是米家的 Aqara 系列,经由米家多功能网关控制。使用 homebridge-aqara 插件即可将小米网关添加到 Homebridge 上。

插件安装完成后,需要在配置文件里填入网关的协议密码和 MAC 地址,获取这些信息需要用到一个小窍门:首先打开米家 App(iOS / Android 版本均可)并绑定网关之后,点击网关界面右上角的 ··· 按钮进入“关于”界面,接着快速连续点击界面底部的空白区域,直到列表中出现“局域网通信协议”和“网关信息”的选项;接下来进入通信协议界面,打开上面的开关即可看到协议密码;MAC 地址则藏在“网关信息”界面里的那堆 JSON 格式的信息中。将这些信息一起复制到插件的配置文件里,保存并重启树莓派即可。由于之前已经将 Homebridge 添加到了 HomeKit 里,现在新加入的米家设备都会自动同步过来。

至此,我已经可以用 HomeKit 控制我家几乎所有的电器了,但这还仅仅是个开始。随着 iOS 11 的发布,HomeKit 平台也得到了进一步加强,同时相关协议也逐渐公开了,相信以后会变得更智能、更好用。借助 HA 和 HomeKit 这两个强大的平台,可以充分发挥想象力来实现一些很有意思的玩法。例如配合人体感应器实现走进房间时自动开灯;通过设置情景模式实现跟 Siri 说“我要去洗澡了”即可自动关窗帘和开浴室灯等等;此外,我还用 Sonoff 智能开关、淘宝二十元买的电磁水阀和五金店买的几截水管 DIY 了一个自动滴灌系统,出门在外时也可以远程操控它给家里的花花草草浇水。

题外话

除了 HomeKit 外,HA 还能配合 IFTTT 和 Workflow 等效率工具以及各种开源项目实现更高级的用法,以及通过 RESTful API 实现与各种编程语言的双向通讯,使自己编写家庭控制程序成为可能,即使你没有 iOS 设备也可以愉快地玩耍。

比如我就随手写了一个 Linux 的 HA 客户端,当我想在 macOS 上关闭卧室空调时只需输入如下命令即可。由于我经常开着 Terminal,所以用这个比打开浏览器再进入 HA 的管理页面控制要方便得多(同时还附带满分装逼效果)。

我还更进一步,用 Python 写了个小程序来实现更智能的自动化功能,它除了有根据实时天气自动调整室内的灯光之类的基础功能外,还可以和我日常生活中的其他方面连接起来,比如当我的网站或者 Vary 的服务器出现异常时,会将我房间的灯全部调成红色以作警示;当手机定位我和家人都不在家,但人体感应器又检测到有人活动的时候(有贼!)就把家里所有的灯都改成阴森昏暗的蓝绿色……由于本文是以 HomeKit 为题,这些高级玩法就不详细展开了,如果大家感兴趣我就找时间再单独写一篇文章谈谈吧。

九个月的努力

在过去九个月里,我一直在忙着做个小项目。经常关注我动态的朋友们应该已经知道我做的是一个社交 App,但至于它是什么样的,我一直有意保密,所以真正了解细节的人屈指可数。不过随着第一个版本的开发工作即将完成,现在是时候揭开它的神秘面纱了。

在这个信息大爆炸的时代,想在网上和好友分享点东西已经有了太多选择。各种社交网络看起来似乎完全不同,有着不同的功能特色、满足着不同的需求,但实际上它们都有着一个共同点。下图右侧是常见的几款社交类 App 的界面,左侧是我将它们抽象化之后的样子,不难发现其实它们本质上其实都是一样的:上面是你的头像和昵称,然后是文字内容,或许还会有张图片。这些元素组合起来就是一条信息,把很多条这样的信息从上到下叠在一起就成为了我们熟悉的信息流。

界面抽象化之后,其实都是一样的

信息流几乎是所有社交类 UGC 产品的核心。近十年以来,互联网已经发生了翻天覆地的变化,信息流却没有过多少根本性的改变。这种信息流的优势在于可以让你在非常短的时间内快速浏览大量碎片化的信息,起初感觉挺爽的,但现在越来越多人逐渐意识到信息太过碎片化并不是什么好事。至于碎片化究竟是好还是坏,我在这里就不作评判了,不过随着 Twitter 和微博相继放开 140 字符的限制,相信答案还是显而易见的。

限制放开之后,尴尬的情况就出现了:传统的信息流并不适应较长的内容。因为它是一维的,内容只能朝着一个方向扩展,所以内容太长就会打破整体的浏览节奏。对于这个问题,通常的解决方法是在信息流里仅显示少量预览内容,点击之后才展开或跳转到新页面查看全文。当然还有更简单粗暴的解决方法:把文字都拼到长长的渣画质图片里发布,也就是我们熟悉的长微博。这些处理方法虽然能解决问题,却一点也不优雅。在我看来,要完美地解决这个问题,唯有完全彻底地重新设计一个信息流。而这,正是我在过去九个月时间里所做的。

传统信息流是一维的,只能朝一个方向扩展

我发现,信息流的长短是由两个维度决定的,分别是时间轴和内容轴。传统信息流将这两个维度压缩进一维的空间里,扩展能力自然会受到限制。毕竟在它出现的时代,我们与用户界面交互时所用的主要设备还是鼠标,而具体到翻阅网页时,则是鼠标的滚轮——只能上下滚动。虽然在移动触屏设备成为主流的今天,这个问题早已不复存在,无论是上下滑动还是左右滑动都是习以为常的操作,但信息流的设计却依然继承着 PC 时代遗留下来的传统操作习惯,未能充分利用触摸屏幕的优势。

现在,是时候改变一下了。

我发明了一种全新的二维信息流,它可以往水平和垂直两个维度扩展,横向是时间,纵向是内容。这种经过“升维”的设计完全突破了内容长度的限制。用户的发布内容显示在长度可变的卡片里,左右滑动浏览不同的卡片,上下滑动查看单张卡片的内容。

二维信息流可朝两个方向扩展,彻底摆脱单条内容的长度限制

既然有理论上可以承载无限长度内容的信息流,如何充分利用它?这就要说到我的第二大创新——模块化内容。也就是说,卡片里的所有内容都是由模块组成的,每种模块都有特定的功能,比如想发一段文字,就用文字模块;要分享一组照片,则可以用图片模块。我目前已经开发了文字、图片、视频、语音和位置模块,还有更多模块正在设计和开发中。

这些模块可以任意添加、排序及组合。每个模块也有许多可自定义的细节,比如文字的字体、字号和对齐方式,或者图片的形状和排列方式等等;当你分享所在位置时,甚至连地图的缩放比例都可以调节。

不过,要是有些时候懒得调整那么多细节,只想简单快速地分享些东西呢?没问题。我开发了一个自动排版引擎,它会分析每张卡片的内容与模块组合情况,动态计算与调整各个模块的样式、尺寸和间距等几十项参数。无论你输入的是短短几个字,还是包含许多模块的复杂内容,都能自动为你生成一张好看的卡片。

智能的排版渲染配合高度个性化的模块化设计彻底摆脱了固定格式的束缚,让你充分发挥自己的想象力,分享不拘一格。“变化”是这个 App 的核心,所以我给它起的名字就是:Vary。

Vary 卡片效果展示

Vary 是一个定位于在熟识好友间分享生活的社交网络,重点是私密和清净。在这里,没有网站链接,没有广告和软文,没有代购和营销,没有无谓的鸡汤,也没有繁重的人际关系。只是在喧闹纷乱的网络世界里,一个可以安安静静地与你在乎的、真正关心你的人分享和记录自己生活的地方。

如今的互联网世界,已经有了太多助你结识陌生人的社交产品,但专为私密好友间交流的却寥寥可数。在我看来,这很大程度上是因为普遍观点认为这种产品难以大量聚拢用户和维持活跃度,若从商业角度出发,无论是大型互联网公司还是创业公司都不太会优先考虑这种产品。然而 Vary 是我出于兴趣爱好而制作的个人作品,商业利益并不是我的首要考虑,因此在这里你会看到许多与主流互联网产品“反其道而行”的设计:使用纯数字帐号体系,只有知道这个帐号的人才能向你发送好友请求,除此之外无法通过其他途径搜索到你;只有你同意与对方成为好友后,对方才能看到你发布的卡片;不会通过匹配手机或邮箱向你推荐那些你可能认识(但永远不想加为好友)的人;你发出的评论和点赞也只有接收者可见。总而言之,一切尽在你的掌控之中。

此外,在 Vary 上可添加的好友数量是有限的,这个上限数量会随着你的使用情况逐渐增加。如果你想让更多人看到你创作的卡片,可以用内置的分享功能将卡片分享到微博、微信或者其他社交网络,也可以生成分享链接直接发送给别人。这个分享功能是开发过程中的一大技术难题,我优化与调整了无数次,才让卡片内容在不同环境里的显示都能够尽量接近原生 App 里的效果。

Vary 卡片外部分享效果

我是一个极简主义者,相信这点已在 Vary 的设计上体现得淋漓尽致。通常简化设计的思维方式是做减法,即思考现有的东西里哪些是可以去除的;而我则喜欢从零开始做加法:彻底放下成见,从根本上思考每一个元素存在的必要性。这种思维方式在 Vary 的界面、交互和功能上都有所体现,比如注册帐号时,你需要填写的东西精简到了极致:不需要任何个人信息,也不用验证激活,只要设置一个登录密码——没错,只填个密码就能完成注册并开始使用,比第三方帐号登录还方便。

同时我也在不断研究如何通过技术手段来引导交互层面的创新,例如 Vary 的 iOS App 可以检测手指接触屏幕的面积,并以此来调整卡片的滚动速度:用指尖滑动时,一次只会滚动一张卡片;整个手指贴在屏幕上滑动时,则会根据你滑动速度的惯性来连续滚动多张卡片。这听起来也许有些玄乎,却非常容易上手,也很实用。

界面设计方面,自然是我代表性的简约风格,并采用以高冷暗色系为主的纯黑白设计,除极少数 UI 元素外再无任何颜色,层次感由灰度、透明度与模糊效果来体现。我一向注重细节,Vary 的界面细节更是以艺术品的标准来要求的,每一个图标、每一帧动画都由我亲手绘制并在程序中实现,处处细节都经过反复打磨,尽可能追求完美。若从理性的商业角度出发,这种程度的追求几乎是不可理喻的,因为它的投入产出比简直惨不忍睹,但我并不在乎。

Vary 主界面

看过我个人网站的朋友们应该都知道,设计和编程是我的两大爱好,我也一直在探索如何将它们更好地结合起来。过去几年里,我每年都会彻底重新设计和开发一次我的个人主页,以展示我在过去的一年里所学会的新东西。然而 Vary 的开发,则是将过去这些经验全部整合在一起,再闭关修炼了一年多之后,才得以协调使用六种计算机语言和十余种开源框架来实现的。

经过一个多月的测试和细节打磨,目前 Vary 的 iOS 版本已经上架 App Store,同时 Android 版本正在开发中,争取六月份上线。初版仍有些不足之处,我会在后续版本中逐渐完善,希望大家多提些建议。

最后,我开通了一个微信公众号,任何与 Vary 相关的事情都可以在这里和我交流,以及提交建议和 bug 反馈等等,有问必答。我也会将 Vary 的最新动态通过这个公众号推送给大家。如果你是一位 iOS / Android / Web 开发者,有意愿参与 Vary 的开发,也可以和我谈谈。总而言之,如果你对 Vary 感兴趣,就在微信搜索 VaryApp 或者扫描下方的二维码关注一下吧。期待你的反馈。

微信订阅号 VaryApp 的二维码

Workflow 实现签到应用客户端

Workflow 是 iOS 平台里一个扩展性极强的自动流程 app,可以自定义一系列复杂操作并把它们编排成脚本,之后便可一键快速执行。网上虽然流传着不少 Workflow 脚本,但大多都仅用于简化日常操作或者实现一些扩展功能而已,经过一段时间的体验,我发现它的潜力远远不止于此:只要有些编程基础,完全可以把它当作一个简单的可视化脚本编程工具来用。这就意味着,可以用它实现一些原本需要真正的编程语言才能实现的东西,比如一个记录当前地理位置的应用。

blog-homepage-with-location-highlight-and-workflow-icon

如果你在用电脑阅读这篇博文,你可以在屏幕左侧看到一个显示我当前所在地的模块(如果你看不到就说明我已经回家了,看上面的图片吧)。我每次到达一个新城市后,在手机上“签到”一下就会更新这里显示的所在地,并在我个人主页的旅行足迹地图加上相关信息。此前这个签到功能是通过一个 Web App 实现的:用 Javascript 通过 HTML5 的 Geolocation API 调用手机 GPS 定位获取经纬度,然后使用第三方接口来将经纬度转换为具体的城市,最后再用 AJAX 提交到我的服务器保存。我每次签到都需要在浏览器中打开签到页面,输入密码登录之后才能“一键签到”。这个过程虽然算不上繁琐,却也并不简便,当然,如果用 Swift 写一个原生 app 来实现,用起来会更方便一些,然而为了“更方便点”专门写个 app 似乎又不太值得……于是这事就不了了之了,直到我用上 Workflow 才真正解决这个问题。这听起来很神奇,实现起来却并不复杂,在这里我会尽量用通俗易懂语言和大家分享一下我的实现方法。

获取定位信息

首先添加一个 Get Current Location 动作,并将返回的结果保存为一个变量,我这里将它命名为 location。

img_1217.jpg

接下来读取这个变量,并通过 Get Details of Locations 动作来获取城市名,然后将结果保存为一个名叫 city 的变量。当然只有城市名是不够的,所以之后还要重复几次这个流程,来获取省份、国家名称和经纬度等信息,并将它们分别保存到变量里。

img_1218.jpg

发送数据到服务器

现在我们已经获取到了所需的信息,接下来需要把它们发送给我服务器的签到接口。这个接口接受 HTTP GET 请求,所以简单拼接 URL 并访问即可发送数据,就像这样:

https://api.dandyweng.com/.../checkin?lat=纬度&lng=经度&city=城市名&state=省份名&country=国家名

这里我先用变量中的信息拼接好 URL,之后用 Get Contents of URL 来发送请求,最后用 Show Notification 来显示服务器返回的内容。

img_1219.jpg

运行测试

大致功能已经实现,现在把它保存成脚本,添加进通知中心的 Widgets 里试试看。

img_1223.jpg

点击之后脚本就开始在后台执行,不需要打开任何 app,几秒钟就执行完毕并显示服务器端返回的成功提示。

这时在服务器端查询数据库,已经可以看到新的签到记录,我个人主页上的旅行足迹地图和这个博客上的所在地信息也已经同步更新。

提升安全性

虽然已经实现了签到功能,但在正式投入使用前还有一个非常重要的问题要解决,那就是安全性。按照目前的实现方式,任何一个知道上面的 URL 和参数的人都可以通过这个接口来提交数据,有非常大的安全隐患。解决的方式是加个密码,但密码通过 GET 方式明文传输依然有可能被截获和盗用,所以并没有什么意义,因此这里要用复杂些的对称密钥加密的方法来进行校验。

首先,生成一个九位数的随机数字,保存为 rand 变量;之后再设置一个密码,保存为 key 变量;最后把随机数和密码拼接在一起,进行 MD5 加密,然后将密文保存为 token 变量。

img_1489.jpg img_1489.jpg img_1490.jpg

最后把 token 的密文和 rand 的明文放在 HTTP 请求里一起发送。服务器收到 rand 参数后,使用相同的步骤和预置的密码 key 来重新生成另一个 token,并检测与 Workflow 里生成的 token 是否一致,若是就表明前一步中填写的密码是正确的,否则就拒绝签到请求。这里的 token 每次都会变,只要密码 key 和随机数 rand 相同,在服务器端生成的 token 就必然是相同的,这里密码本身并不会被传输,从而避免被截获和盗用的风险。

至此这个“地理位置签到应用 iOS 客户端”就已经开发完成,根据我最近几天的使用体验,总结起来就一个字:爽!大家有兴趣也可以自己试试看,有任何问题都可以在这里留下评论,我会尽量解答。

❌