玩玩智能家居1:Domoticz

1. 什么是 Domoticz

第一次知道 Domoticz 是在 smzdm 上的一篇原创文章《开源IoT平台domoticz与百搭wifi模块esp8266 篇三:空气站 Weather Station》里,以及知道了 ESP8266 模块这个好东西。

Domoticz 支持众多 USB 以及串口设备,可以通过将 Domoticz 安装在树莓派上,直接读取和操作连接在树莓派上的设备,例如 RFLink 或者 I2C 接口的 BME280 传感器。

当然,Domoticz 还支持通过局域网控制的设备和有网络 API 的设备,例如小米网关或者 Netatmo Weather Station。

2. 主要功能

传感器

Domoticz 可以直观的展示各种传感器状态以及历史,常用的有温度、湿度、二氧化碳浓度等。

对于 Domoticz 没有内置类型的传感器,也可以使用 Custom Sensor 来展示,可以自定义单位,同样可以查看历史。

开关

对于灯、遥控器等,都可以通过 Domoticz 开关类型设备来实现控制。对于遥控器,Selector Switch 是个好选择。

事件

Domoticz 内建了事件系统,对于事件的处理,简单的方法可以使用 Blockly 图形化编程来实现各个传感器和设备间的联动,如果有复杂逻辑,可以直接使用 Lua 脚本来处理。这给用户带来更大的自由度。

3. 对比其他 Home Automation System

当然,开源 Home Automation System 有很多,但是对比使用了一下其他系统之后,觉得相对而言还是 Domoticz 更简单易用一些。

openHAB

openHAB 是一个使用 Java 编写的开源 Home Automation System。它也支持相当多的设备,很容易配置连接 Amazon Dash Button、Philips Hue 等设备,还支持连接 Kodi 这个多媒体中心。

但是在使用的时候,openHAB 添加设备并不是特别直观,也没有弄明白自己怎么样去添加一个自定义的传感器设备。另外怎么查阅传感器历史数据也没有找到入口。

Home Assistant

Home Assistant 是一个开源的,使用 Python 3 编写的 Home Automation Platform。

对于我来说,最不喜欢 Home Assistant 的一点就是,它的设备都是需要使用 YML 配置文件的方式来配置,这在使用过程中感觉不是很方便。

4. 优缺点

对于我来说,第一步需要做的是使用 ESP8266 构建家庭传感器网络,在对比了几个系统之后,最终选择 Domoticz 作为 Home Automation System。

Domoticz 的优点

  • 直接在网页界面配置设备
  • 很容易添加自定义传感器设备
  • ESPEasy 内置 Domoticz 数据传送支持
  • Lua 事件处理比较强大

Domoticz 缺点

  • 界面不够现代,虽然 elemental 主题稍微好一点
  • 官方 App 功能不够完全
  • Homebridge 插件 homebridge-eDomoticz 功能不完善

有些缺点其实也是可以克服的,官方 App 可以用网页版代替,Domoticz 的主题本身带有移动设备支持,而 homebridge-eDomoticz 插件的问题,就只能靠自己改代码来完善了。

5. 安装

Domoticz 官方提供了 Windows、macOS 以及 Linux 的二进制文件,对树莓派也提供了编译好的二进制包,所以安装起来还是很方便的。另外官方还提供了树莓派和 Ubuntu 的一键安装脚本,比使用压缩包更省事。

因为不想另外找个树莓派或者开一台虚拟机,我就在 DSM 的 Docker 中部署了 Domoticz。Domoticz 的 Image 在 Docker Hub 上很容易找到。

6. 添加设备

Domoticz 提供了很多类型的硬件,其中一些很便于扩展使用自己的设备。

虚拟设备

在 Hardware 中添加一个 Dummy 的硬件,然后从这个硬件就可以创建各种虚拟传感器了,在 ESPEasy 中可以很方便的更新这些虚拟传感器的数据。

HTTP/HTTPS Poller

使用 HTTPS Poller 这个设备就可以很方便的去拉取一些外部数据来显示在 Domoticz 内部了,比如去获取外部的 AQI 指数。

小米网关

在去年年底,Domoticz 加入了小米多功能网关的支持,通过 Xiaomi Gateway 这个硬件,可以自动的将小米多功能网关的子设备(例如温湿度传感器、门窗传感器、人体活动传感器以及小米智能插座)添加到 Domoticz 中。

7. 事件

借助于 Blockly 或者 Lua,可以很方便地实现家中设备的联动,例如开门时自动打开小米智能插座。

8. 参考资料

  1. 5 open source home automation tools
  2. 开源IoT平台domoticz与百搭wifi模块esp8266 篇三:空气站 Weather Station
  3. openHAB
  4. Home Assistant

未经许可,禁止转载

— EOF —

玩玩智能家居0:开始折腾

记录一下折腾智能家居的过程和东西。

Domoticz

Domoticz 是一个开源的 Home Automation System,使用 C++ 编写,相对于其他系统的好处就是占用资源少。

Domoticz 使用比较简单,界面也比较清晰,不过界面不太现代。

NodeMCU 和 ESP8266

ESP8266 是上海乐鑫出的一款自带 Wi-Fi 的 MCU,NodeMCU 是基于 ESP8266 ESP-12E 做的一个开发板,集成了 Micro USB 输入,4M Flash,开发时使用起来更方便。

NodeMCU 默认是使用 Lua 作为开发语言的,但是借助于 ESP8266 Arduino 项目,就可以拿 NodeMCU 当 Arduino 开发板来使用了,可以直接使用 Arduino IDE 或者 PlatformIO 来开发。

ESPEasy

ESPEasy 是一款可以用于 ESP8266 芯片的开源固件,使用这个固件可以很方便的把常用的一些传感器集成到 Domoticz 系统中。

并且 ESPEasy 内置 Web 配置界面,以及一整套日志、插件、数据传输系统,非常方便于我这个 Arudino 新手来使用。

BroadLink

BroadLink 是杭州的一家智能家居产品厂商,出了一系列的智能家居产品,例如门磁、红外传感器、智能插座、万能遥控等。

在刚开始玩智能家居的时候,BroadLink 是唯一一家有全套产品的品牌,于是买了一些它们家的产品。在使用 Domoticz 后也尝试将它们家的产品集成到这个 Domoticz 中。

小米多功能网关

小米在去年也推出了相当于的智能家居附件,但是一开始并没有选用小米的产品。在去年末小米多功能网关推出局域网通信协议之后,Domoticz 也迅速加入多功能网关的支持,这让我决定来尝试一下小米的产品。

在购买收货之后,发现相对于 BroadLink 来说,小米的产品在外观及体积上有相当大的优势,于是决定用小米的传感器来替代 BroadLink 的传感器产品。

Homebridge

Homebridge 是一个开源的 HomeKit 网桥实现,可以很方便的集成各种设备到 HomeKit 中,从而可以使用 Hey Siri 来控制各种设备。

使用 Homebridge-eDomoticz 插件可以很方便的将 Domoticz 中的设备导入到 Homebridge 中,这样除了使用 Web 界面来控制设备,还可以使用语言来控制了。

参考资料

  1. Domoticz
  2. ESP8266
  3. NodeMCU
  4. ESP8266 Arduino Core
  5. ESPEasy
  6. BroadLink
  7. 小米多功能网关
  8. Homebridge

未经许可禁止转载

— EOF —

使用 python-broadlink 发送 BroadLink A1 数据到 Domoticz

之前在 smzdm 上看一些物联网的文章时,看到 Domoticz 这个平台,好像还蛮符合我的需求的,文章里还介绍了使用 RMBridge 来使用 Domoticz 来控制 RM Pro。

但是 RMBridge 并不是特别方便,需要一直跑一个 Android 来作为指令中转,于是在 GitHub 上去找找看有没有其他项目来支持 BroadLink 的 RM 设备,然后就找到了 python-broadlink 这个项目。

在研究 python-broadlink 的代码过程中,发现它同时还支持 BroadLink 的 A1 空气质量监测仪,刚好我就有一个 A1 设备,于是准备试试看能不能把 A1 的数据更新到 Domoticz 平台上。

最终的效果如下图所示:

准备工作

因为我有一个 DSM,并且 Domoticz 有现成的 DSM Package,所以所有工作都是基于 DSM 完成的,需要的软件如下:

  • DSM 6
  • Domoticz v3.5876
  • python-broadlink

创建设备及虚拟传感器

安装完 Domoticz 后,需要创建虚拟设备以及虚拟传感器来接受数据。

在“设置》硬件”中创建一个虚拟设备,只用来创建虚拟传感器:

在刚刚创建的 BroadLink A1 设备上,选创建虚拟传感器,并选择“温度+湿度”:

使用同样的方式来创建两个“文本”类型的虚拟传感器,用来显示光照和声音。

创建完虚拟传感器后,可以在“设置》设备”中看到刚刚创建的虚拟传感器,这里需要记住的是“Idx”这一档,这在后面的代码中需要用到:

在 DSM 上部署更新脚本

先在 DSM 中创建一个共享文件夹 domoticz,用于存放相关的代码文件。

下载 python-broadlink 项目代码,将 broadlink 目录上传至 domoticz 共享文件夹中。

使用 DSM 文本编辑器,在 domoticz 共享文件夹中创建一个新的文件 broadlink-a1.py,内容如下:

#!/usr/bin/python
# -*- encoding: utf-8 -*-

A1_IP = '1.1.1.1' # 修改为 BroadLink A1 IP
A1_MAC = '00:00:00:00:00:00' # 修改为 BroadLink A1 MAC 地址
DOMOTICZ_IP = '1.1.1.1' # 修改为 Domoticz 服务器 IP
DOMOTICZ_PORT = 8084 # 修改为 Domoticz 服务端口

DEV_HUM_TEMP_IDX = 1 # 修改为“温度+湿度”虚拟传感器Idx
DEV_NOISE_IDX = 2 # 修改为“声音”虚拟传感器Idx
DEV_LIGHT_IDX = 3 #修改为“光照”虚拟传感器Idx

import broadlink
import requests
import urllib

a1 = broadlink.a1((A1_IP, 80), A1_MAC)
a1.auth()
data = a1.check_sensors_raw()

hum = data['humidity']
hum_stat = 0
if 45 < = hum <= 60:
  hum_stat = 1
elif hum < 40:
  hum_stat = 2
elif hum > 70:
  hum_stat = 3

# update temp and hum
url = "http://%s:%d/json.htm?type=command¶m=udevice&idx=%d&nvalue=0&svalue=%0.1f;%0.1f;%d" % (DOMOTICZ_IP, DOMOTICZ_PORT, DEV_HUM_TEMP_IDX, data['temperature'], hum, hum_stat)
requests.get(url)

# update noise
db = '未知'
noise = data['noise']
if noise == 0:
  db = '寂静'
elif noise == 1:
  db = '正常'
elif noise == 2:
  db = '吵闹'
db = urllib.quote(db)
url = "http://%s:%d/json.htm?type=command¶m=udevice&idx=%d&nvalue=0&svalue=%s" % (DOMOTICZ_IP, DOMOTICZ_PORT, DEV_NOISE_IDX, db)
requests.get(url)

# update light
light = data['light']
lux = '未知'
if light == 0:
  lux = '黑暗'
elif light == 1:
  lux = '昏暗'
elif light == 2:
  lux = '正常'
elif light == 3:
  lux = '明亮'
lux = urllib.quote(lux)
url = "http://%s:%d/json.htm?type=command¶m=udevice&idx=%d&nvalue=0&svalue=%s" % (DOMOTICZ_IP, DOMOTICZ_PORT, DEV_LIGHT_IDX, lux)
requests.get(url)

请注意需要修改对应 BroadLink A1 的 IP 以及 MAC 地址,以及 Domoticz 安装服务器的地址和端口,如果是以 DSM Package 的形式安装的,那么 IP 与 DSM 的 IP 同样,端口默认为 8084。

之后 domoticz 共享文件的目录应该是这样的:

添加计划任务定时更新 A1 数据

为了能定时更新 A1 数据到 Domoticz,需要在 DSM 中添加一个计划任务,按时运行 broadlink-a1.py 这个脚本。

在 DSM 的“控制面板”中找到“任务计划”,添加一个“计划的任务》用户定义的脚本”,将任务设置为每5分钟运行一次,并且任务设置中运行命令按如下设置:

为了测试脚本是否正确,可以在保存完任务后,选中刚刚创建的任务手动运行一次,如果数据正常上传到 Domoticz 的服务器,那么在 Domoticz 的“设置》设置”中可以看到对应虚拟传感器的最后通讯时间为刚刚运行任务的时间。

如果任务正确运行,也可以在 Domoticz 的温度面板中,也可以看到虚拟传感器会显示正确的温度以及湿度。

至此,所有工作已经完成,Domoticz 可以实时显示 A1 的数据并且记录曲线。

参考资料

  1. 开源IoT平台domoticz与百搭wifi模块esp8266 篇二:domoticz与broadlink的联接
  2. python-broadlink by mjg59
  3. Domoticz API/JSON URL’s

— EOF —

使用 KindleEar 定时推送《知乎日报》到 Kindle Paperwhite

入手 Kindle Paperwhite 已经有段时间了,但是一直没有充分利用起来。

HiPDA! 的 E-ink 版乱逛的时候,看到 cdhigh 开源了一个 Kindle 推送服务的代码 KindleEar,而最近又经常会去看知乎日报,于是就想能不能每天定时把知乎日报推送到 Kindle 上阅读。

在看了 KindleEar 的代码之后,发现添加一种订阅源还是比较简单的,于是直接拷贝了原来 books 中的代码,写了一个知乎日报的模块 ZhihuDaily.py

具体的代码可以前往我的 fork:,我也已经提交了 Pull Request 给原作者。

不过在测试过程中发现知乎屏蔽了来自 Google App Engine 的网页获取请求,经过 @clowwindy 说明得知有人滥用 Google App Engine 于是简单的屏蔽了 GAE 的访问。

当然这个不难解决,通过另外一个服务器中转一下知乎日报的 Feed 就行了,知乎并没有屏蔽所有外部访问⋯⋯

于是写了个 ZhihuDailyForwarder,将这个项目部署到 appfog 上,再将 ZhihuDaily.py 中原来的 API 地址替换成 appfog 上的项目首页地址,就可以正常的采集了。

完整部署的步骤:

  1. 下载 ZhihuDailyForwarder 项目代码,部署到 appfog 或其他支持 nodejs 的服务器上
  2. 下载

项目代码,参照 readme.txt 中说明,修改 app.yaml 及 config.py 中相关设置

  • 进入 KindleEar 项目文件夹,修改 books/ZhihuDaily.py,修改 http://news.at.zhihu.com/api/1.1/news/latest 为第一步中部署完毕的 ZhihuDailyForwarder 首页地址,例如 http://example-zhihu.aws.af.cm
  • 部署 KindleEar 到 Google App Engine
  • 进入部署完毕的 KindleEar 网站,设置自己的 Kindle 邮箱,并订阅知乎日报,当然别忘了在 Amazon 那边设置允许发送邮件的邮箱地址为 Google App Engine 的账号邮箱地址。
  • PS. 知乎并没有正式开放知乎日报的 API,目前使用的 API 为网友嗅探而来,随时有可能被停用,所以不能保证这个功能长期有效。

    感谢 cdhigh 带来的 KindleEar 项目。

    希望此文对 Kindle 用户和知乎日报爱好者有所帮助。

    参考资料

    1. http://www.hi-pda.com/forum/viewthread.php?tid=1213082
    2. https://gist.github.com/zellux/5844688
    3. https://github.com/ohdarling/KindleEar
    4. https://github.com/cdhigh/KindleEar
    5. 知乎日报 Feed 转发服务 nodejs 版本

    —EOF—

    [笔记] Objective-C 中模拟泛型及 Xcode 格式化代码插件

    记录一些 iOS & OS X 开发过程中学到或有趣的东西。

    Objective-C 中模拟泛型

    今天看到的一个辅助代码,可以在 Objective-C 代码中添加泛型的支持:

    泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部份,那些部份在使用前必須作出指明。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。(via )

    泛型一般出现在 .NET、Java 或 C++ 的代码里面。

    在 Objective-C 里面,像数组之类的容器,在取单个元素时,返回类型都是 id,这个时候如果要使用返回对象的一些属性,就需要先转换一下类型,或者使用 getter 方法来获取。

    如果用了泛型之后,就很方便了,可以直接以 arr.lastObject.prop 的方式来访问。

    使用了上面这个代码使 Objective-C 支持泛型后,就可以像下面这样写代码了:

    generics example

    这个辅助代码是一个大宏,给 Objective-C 添加泛型支持是以以下几个方面来做到的,例如给 MyClass 添加泛型支持来说明:

    1. 定义一个 MyClass 的 Protocol
    2. 给各个容器类(例如 NSArray、NSMutableArray、NSSet)添加 Category,重新定义各个容器操作方法的返回值和参数类型
      1. 获取单个元素的方法返回值类型修改为 MyClass *
      2. 返回容器的方法返回值类型修改为 NSArray<myclass> *
      3. 参数类型同样处理

    这样在使用 NSMutableArray</myclass><myclass> *arr 这样一个变量时,如果添加的元素不是 MyClass * 类型,就会发出警告了。

    不过因为 Objective-C 在编译时并不会强制要求变量类型一致,所以如果传递一个非 MyClass *类型的参数,也是可以编译通过的。

    当然这个库只能说是在一定程度上方便编写代码而已,至于用不用还是见仁见智了,每次要写 NSArray</myclass><myclass *> 也不见得更方便。

    Xcode 代码格式化插件 BBUncrustifyPlugin

    插件地址:

    这个插件算是对 uncrustify 的一个快捷引用了。

    可能会比 Xcode 自带的 Re-indent 功能强大一点点 :)

    另外,还有一个图形化的参数配置工具,可以用来配置 uncrustify 那好几百项目的参数:UncrustifyX

    参考资料

    1. http://iosdevelopertips.com/objective-c/generics-in-objective-c.html
    2. https://github.com/tomersh/Objective-C-Generics
    3. https://github.com/benoitsan/BBUncrustifyPlugin-Xcode
    4. http://zh.wikipedia.org/wiki/%E6%B3%9B%E5%9E%8B
    5. http://uncrustify.sourceforge.net

    —EOF—

    在 Sandboxed Mac App 中嵌入第三方可执行文件

    之前开源了 一个 gitstats 的 GUI 应用 GitStatX,在提交到 GitHub (GitStatX) 之后,又准备提交到 Mac App Store。

    在提交到 Mac App Store 之后,出现了一些问题,程序中包含的第三方可执行文件没有签名,导致苹果拒绝了提交的程序包:

    App sandbox not enabled – The following executables must include the “com.apple.security.app-sandbox” entitlement with a Boolean value of true in the entitlements property list. Refer to the App Sandbox page for more information on sandboxing your app.

    • GitStatX.app/Contents/Resources/git/bin/git
    • GitStatX.app/Contents/Resources/gnuplot/gnuplot

    但是 GitStatX 所包含的 git 以及 gnuplot,并不是我程序中的代码,也没有 Xcode 工程去使用一个 entitlements 文件来指定它为启用 sandbox 状态。

    所幸在网上搜索的时候,找到了可以使用 codesign 工具来进行签名的方法。

    检查可执行文件是否启用 sandbox

    codesign --display --entitlements - ./commandlinetool
    

    给可执行文件签名并启用 sandbox

    先在命令行工具同目录创建一个 entitlements.plist:

    < ?xml version="1.0" encoding="UTF-8"?>
    < !DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>com.apple.security.app-sandbox</key>
        <true></true>
        <key>com.apple.security.inherit</key>
        <true></true>
    </dict>
    </plist>
    

    这里设置了 com.apple.security.app-sandbox 为 true 来启用 sandbox。

    然后使用 codesign 进行签名:

    codesign -s "3rd Party Mac Developer Application: Your Name" --entitlements ./entitlements.plist ./commandlinetool
    

    记得把 “3rd Party Mac Developer Application: Your Name” 替换为实际的证书名称。

    问题

    在给 gnuplot 签名之后,提交到 Mac App Store,苹果还是会自动验证并发邮件说 gnuplot 没有签名,于是在本地直接导出 GitStatX.app,并检查了一下,发现 gnuplot 的 entitlements 又没有了,但是 git 的 entitlements 还是保留的。在 gnuplot 同目录下,有一个 _CodeSignature 目录,可能是在打包的时候会自动处理。

    为了避免这个问题,我就把 gnuplot 也放到了一个 bin 目录下,然后再打包并检查,发现 gnuplot 已经是正确签名并且保留有 entitlements 的了。

    当然,GitStatX 最终也正确提交到 Mac App Store,并且程序进入了 Waiting For Review 状态。

    参考资料

    1. Mac OS app, sandbox with command line tool?
    2. Checking Code Signing and Sandboxing Status in Code
    3. Entitlement Key Reference

    — EOF —

    开源 GitStatX:一个 gitstats 的 GUI 应用程序

    GitStatX 是一个 gitstatx 的 GUI 应用程序,用于方便在 Mac OS X 中使用 gitstats。

    一般情况下,如果要在 Mac OS X 上使用 gitstats,需要自行安装 gnuplot,而这需要使用 macports 或者 homebrew,通常这会比较费时间,并且只能使用命令行来使用 gitstats 生成所对应 git 仓库的报告。

    GitStatX 提供了一个 GUI 来使用 gitstats,具备以下功能:

    • 同时管理多个项目
    • 使用分组来归类各个项目
    • 标识项目类型
    • 自动生成报告
    • 导出仓库的活动报告

    截图

    项目主页

    仓库地址

    下载

    查看所有下载

    授权

    本软件及代码以 GPLv3 授权发行。

    相关代码

    • 使用 bootstrap 样式的 gitstats:

  • 修正可执行文件依赖库的脚本:
  • 联系我

    — EOF —

    使用 ukraine 建设 node.js 私有云

    源由

    node.js 越来越流行,托管 node.js 应用的云服务也越来越多,例如 nodejitsuheroku 等。

    但是这些云服务通常有这样那样的限制,又或者是要收费的。而有些时候我通常不需要跑很大的应用,或者是很稳定的应用,只是为了跑一些小的,或者是学习用的 node.js 应用,并且我也有自己的 VPS,想把这些应用托管在自己的服务器上。

    于是我需要去找一个可以在自己的 VPS 上建设一个 node.js 私有云的软件。

    比较

    在看了 中的 DIY Platforms 后,尝试了一下其中介绍的平台:

    • nodester: 安装比较麻烦,不支持新版本的 nodejs,安装说明还是针对 node 0.4.11 的
    • CloudFoundry: 比较庞大,而且是以 vm 方式安装,不适合 VPS
    • OpenShift: 同 CloudFoundry,不只支持 node.js,安装复杂,不适合 VPS
    • Nodejitsu: Nodejitsu 开源了他们所用的 node.js 应用管理项目 haibu,haibu 安装比较简单,而且支持最新的 nodejs 0.8.16,不过 Nodejitsu 同样开源的命令行客户端 jitsu 并不支持 haibu
    • Stagecoach: 文档不够清晰,看了很久也没明白它的架构和怎么部署⋯⋯

    这样看来似乎没有一个可以满足我的需要,不过 GitHub 是强大的,通过搜索找到了 ukraine 这个项目:

    ukraine glues haibu and node-http-proxy adding a little helper, chernobyl, that deploys into this cloud. It is probably as stable as you think it is.

    这就是我想要的。

    修改

    原始的 ukraine 虽然已经基本满足了我的需要,但是还有一些小的功能需要增加:

    1. 使用 nginx 作为前端,这样 node.js 应用可以部署在 nginx 后面,与 PHP 等项目并存
    2. 使用 SSL 保护 haibu 的服务端,防止 auth_token 因为不加密的 HTTP 通信而泄漏
    3. 因为使用 nginx 作为前端,所以 haibu 服务端和 node-http-proxy 都不需要监听 0.0.0.0,而只需要监听 127.0.0.1
    4. 防止 node.js 应用监听了常用端口而导致其他应用启动失败,因为使用了 nginx 作为前端,node.js 应用本身监听了什么端口就不重要了
    5. 防止 node.js 应用直接对外提供服务,同样因为已经有 nginx,node.js 应用只需要监听 127.0.0.1 就行了
    6. chernobyl 不支持配置每个不同的 ukraine 监听在哪个端口,以及有没有配置 SSL
    7. 我想 ukraine 作为一个服务存在,这样在 VPS 启动时可以自动启动
    8. node.js 应用需要支持绑定自定义域名,而不是只能绑定子域名

    所以我 fork 了 radekstepan 的 ukraine 到 ,并做了一些自己需要的修改。

    安装修改后的 ukraine

    如果你和我一样,也需要一个这样简单的 node.js 私云,那么以下的内容可以帮助里部署 ukraine 到自己的 VPS 上。

    注意:安装教程以在 Ubuntu/Debian 上为例,并且所有命令是以 root 用户执行。

    1. 安装 node.js

    haibu 需要 node.js 的版本大于 0.8,所以需要安装最新的 node.js 包,或者自行编译安装。

    参考这篇文章:Installing Node.js via package manager

    2. 安装 forever

    forever 是用来维持 ukraine 一直在启动状态

    npm install forever -g
    

    3. 配置 nodejs 用户

    为了使所有 node.js 应用不使用 root 权限运行,防止出现权限方便的风险,需要添加一个用户 nodejs 来运行 node.js 应用。

    groupadd nodejs
    useradd -g nodejs -m -s /bin/bash nodejs
    

    4. 获取并安装 ukraine

    为了管理方便,这里安装 ukraine 到 /srv/ukraine 中,如果你不安装在这个位置,那么相关的脚本和配置文件都需要修改。

    cd /srv
    git clone https://github.com/ohdarling/ukraine
    cd ukraine
    git checkout private-cloud
    npm install
    chown -R nodejs.nodejs /srv/ukraine
    

    5. 配置 ukraine

    cd /srv/ukraine
    cp config.example.json config.json
    vim config.json
    

    为了安全起见,建议 auth_token 不要留空。

    example.com 需要替换为你自己的域名,这样以后部署了 node.js 应用时,会自动分配一个 package-name.example.com 的子域名。

    6. 安装服务脚本

    注意:这个脚本只适用于 Ubuntu/Debian。

    cd /srv/ukraine
    cp server/init-script/ukraine /etc/init.d/
    chmod +x /etc/init.d/ukraine
    

    7. 使用 nginx 作为前端服务

    为了使 node.js 应用与原有的 PHP 共存,使用 nginx 作为 ukraine 的前端服务。

    注意:部署 node.js 应用到 ukraine 需要 nginx 启用 chunkin 模块,默认情况下 nginx 并没有安装此模块,可以自行编译安装(参考 ),或者直接使用包管理器安装 nginx-extras,这个包中包含的 nginx 已经编译了 chunkin 模块。

    添加以下配置文件内容到 /etc/nginx/sites-available/ukraine,并且在 /etc/nginx/sites-enabled/ 中添加一个到配置文件的符号链接。注意,需要替换配置文件内容中的 haibu.example.com*.example.com 为你自己的域名。

    server {
        listen   80;
        server_name  haibu.example.com;
    
        access_log  /var/log/nginx/localhost.access.log;
    
        chunkin on;
    
        error_page 411 = @my_411_error;
            location @my_411_error {
            chunkin_resume;
        }
    
        location / {
            proxy_pass http://localhost:9002;
            proxy_set_header  X-Real-IP  $remote_addr;
        }
    }
    
    server {
        listen   80;
        server_name  *.example.com;
    
        access_log  /var/log/nginx/localhost.access.log;
    
        location / {
            proxy_pass http://localhost:8000;
            proxy_set_header  X-Real-IP  $remote_addr;
            proxy_set_header Host $host;
        }
    }
    

    建议在 haibu.example.com 这个站点上启动 SSL 来保护 auth_token。

    添加完配置文件后,使用以下命令让 nginx 重新载入配置:

    nginx -s reload
    

    8. 启动 ukraine

    service ukraine start
    

    9. 检查 ukraine 是否正常运行

    打开浏览器,访问 http://haibu.example.com/version,将会看到以下内容:

    {"version":"haibu 0.9.7"}
    

    注意,如果在之前已经配置了 auth_token,将会看到:

    {"message":"Wrong auth token"}
    

    这说明 ukraine 已经正常启动。

    部署自己的 node.js 应用

    首先需要在本地安装 ukraine:

    npm install -g git://github.com/ohdarling/ukraine\#private-cloud
    

    如果之前配置了 auth_token:

    chernobyl config haibu.example.com auth_token=xxxx
    

    如果之前配置了 SSL:

    chernobyl config haibu.example.com https=true
    chernobyl config haibu.example.com haibu_port=443
    

    现在可以部署 node.js 应用了,进入到 node.js 应用的根目录,运行以下命令:

    chernobyl deploy haibu.example.com .
    

    这就会部署这个 node.js 应用到 haibu.example.com 了。

    给 node.js 应用绑定自定义域名

    在给 node.js 绑定自定义域名,只需要在 package.json 中添加 domains 属性即可:

    {
        "name": "example-app",
        "version": "0.0.2",
        "domains": [
            "custom-example.com"
        ]
        "dependencies": {
            "express": "2.5.x"
        },
        "scripts": {
            "start": "server.js"
        }
    }
    

    同样需要修改 nginx 的配置文件,把自定义域名加到 server_name 中:

    server {
        listen   80;
        server_name  *.example.com, custom-example.com;
    
        access_log  /var/log/nginx/localhost.access.log;
    
        location / {
            proxy_pass http://localhost:8000;
            proxy_set_header  X-Real-IP  $remote_addr;
            proxy_set_header Host $host;
        }
    }
    

    注意事项

    1. 所有 node.js 应用会监听在一个随机端口,并且会监听在 127.0.0.1,也就意味着在外部没有办法直接访问这个应用
    2. package.json 中 scripts.start 属性,不需要带 node,只需要指定以哪个脚本启动即可,例如以下是错误的:
        {
            "name" : "example-app",
            "scripts" : {
                "start" : "node server.js"
            }
        }
    
    1. 如果需要 node.js 鉴定特定的端口,并能直接对外服务,可以在 package.json 的 env 属性中添加 “HAIBU_INDEPENDENT_SERVICE”: “true”,例如:
        {
            "name" : "somesocks",
            "scripts" : {
                "start" : "server.js"
            },
            "env" : {
                "HAIBU_INDEPENDENT_SERVICE" : "true"
            }
        }
    

    问题及反馈

    你可以在 GitHub 上 fork 这个仓库:

    联系我

    Twitter: @ohdarling88
    Email: ohdarling88 at gmail.com

    感谢

    参考资料

    1. https://github.com/joyent/node/wiki/Node-Hosting
    2. https://github.com/ohdarling/ukraine
    3. https://github.com/radekstepan/ukraine
    4. https://github.com/nodejitsu/haibu
    5. https://github.com/nodejitsu/haibu-carapace
    6. Installing Node.js via package manager

    — EOF —

    Developer ID Application 在 Mac OS X 10.7.5 下不能启动的问题

    记录一下最近发布阿里旺旺 for Mac 3.0.1 时碰到的问题。

    阿里旺旺在发布到 labs.etao.com 时,为了兼容 Mac App Store 版本,也要加上 Sandbox 支持,所以 App 是使用 Developer ID Application 方式发布的。

    但是有一些人在拿到新版本后,却没办法运行程序,总是会提示以下错误:

    Exception Type:  EXC_BAD_INSTRUCTION (SIGILL)
    Exception Codes: 0x0000000000000001, 0x0000000000000000
    
    Application Specific Information:
    dyld: launch, running initializers
    /usr/lib/libSystem.B.dylib
    xpchelper reply message validation: code signature invalid
    The code signature is not valid: The operation couldn’t be completed. (OSStatus error -67061.)
    
    Application Specific Signatures:
    code signature invalid
    

    一开始以为是签名的问题,然后另外一个同事使用同样方式发布 Developer ID Application 继续不能运行。于是去网上找了一下这个问题,发现了一篇文章《OS X 10.7.5 FAILS TO LAUNCH CODE-SIGNED APPS 》讲到这个问题,说这个可能是因为苹果在 Mac OS X 10.7.5 中改了什么东西导致的,只要重签名一下 App 就可以了。

    打开终端,进入要签名的 App 所在目录,使用以下命令来重新签名:

    codesign -fs 'Developer ID Application' --prefix 'com.taobao' \
    --preserve-metadata=i,e,res,req --timestamp=none AliWangwang.app
    

    注意:如果你的 Keychain Access 里有多个 Developer ID Application 证书的话,需要把 Developer ID Application 替换成 Keychain Access 中完整的 Developer ID Application 证书的名称。

    重新签名之后的 Developer ID Application,就可以在 Mac OS X 10.7.5 上正常打开和使用了。

    参考资料

    1. OS X 10.7.5 FAILS TO LAUNCH CODE-SIGNED APPS

    — EOF —

    一个修正 Mach-O 文件加载共享库路径的脚本

    在 Mac 上用到一些开源的程序,经常需要自己编译,这个时候一般会使用 macports 所提供的工具链进行编译。

    在编译的过程中,开源程序所引用的各种其他库,例如 libssl、libz、libgd 之类的,一般都会在 /opt/local/lib 下,而如果把这个编译好的二进制文件给其他人用时,如果其他人没有安装 macports,那么就会缺失这些共享库,从而导致编译好的二进制程序无法运行。

    具体原因就是因为 Mac 下链接共享库时,会在链接时将共享库的路径写入到最终的二进制文件中,而默认情况下,这个路径是绝对路径,例如 /opt/local/lib/libz.1.dylib。

    所幸苹果提供了 install_name_tool 这个工具来修改共享库的路径。

    下面这个脚本就是用来批量替换二进制文件中的共享库路径。

    这个脚本会在当前目录下创建 lib 目录,将二进制文件所依赖的 /opt/local/lib 中的共享库复制到 lib 目录中,并修改二进制文件中保存的共享库的路径。

    脚本的使用方法,将上面的脚本保存为 fixlibpath.sb,并加上可执行权限,放到要修正的二进制文件所在的目录:

    chmod +x fixlibpath.sh
    ./fixlibpath.sh mybinary
    

    还有共享库会依赖其他共享库的情况,所以也需要对 lib 中复制过来的共享库进行同样的处理:

    for f in lib/*; do
        ./fixlibpath.sh $f
    done
    

    可以观察一下脚本的输出,如果输出的内容中,每个共享库所依赖的库路径中不存在 /opt/local/lib,那就表示处理完成了。每个共享库都会有一个 /opt/local/lib 路径的,但是名称与这个共享库一样的路径,这个是不用处理的。

    要测试处理完成的二进制文件是否正常,可以将该二进制文件和 lib 目录复制到一台没有安装 macports 或者没有 macports 安装相关共享库的机器上运行,如果能正常运行就表示处理成功了。

    参考资料

    1. http://www.mikeash.com/pyblog/friday-qa-2009-11-06-linking-and-install-names.html
    2. http://stackoverflow.com/questions/4677044/how-to-use-dylib-in-mac-os-x-c#answer-11585225

    — EOF —