我喜欢慢跑。有时天气很冷。有时天气很冷,但看起来并不冷。阳光普照,鸟儿鸣叫。然后你穿着短裤和 T 恤走到户外,意识到你大约只有 2 分钟的时间才能避免着凉。
我决定用一个灯泡来解决这个问题,根据外面的温度显示特定的颜色。它的效果比我预期的要好,这说明了一些问题,因为通常情况下,事情都不会像我想的那样发展。
这是一个很有趣的项目,因为它本质上是一个在定时器上运行的托管服务,因此它是 Serverless 的完美用例。
现在你可能在想,“嗯,直接查看天气预报不是更容易吗?” 当然,确实更容易,但那样我就没有借口买一个昂贵的灯泡或写一篇包含“Serverless”这个词的文章了。
所以让我们看看如何构建你自己的天气灯泡。最终代码并不复杂,但它确实有一些值得注意的有趣部分。顺便说一句,我有没有提到它是 Serverless 的?
构建天气灯泡
首先,你需要一个灯泡。没有灯泡就没有天气灯泡。把“灯泡”这个词大声念 10 次,你会发现它是一个多么奇怪的词。灯泡,灯泡,灯泡——看到了吧?很奇怪。
我使用的是 LIFX Mini Color。它并不*太*贵,但更重要的是,它有一个开放的 API。

该 API 有两种身份验证方法。第一种包含“OAuth”这个词,我为让你读到它而感到抱歉。别担心,有一种更简单的方法,不需要涉及 OAu…. 那个不言而喻的东西。
第二种方法是在 LIFX 中注册一个应用程序。你会得到一个密钥,你只需要在任何 HTTP 请求中传递该密钥即可。这就是我用于此演示的方法。
例如,如果我们想将灯泡颜色更改为蓝色,我们可以将color: blue
传递到/state
端点。

该 API 支持几种不同的颜色格式,包括命名颜色(如红色、蓝色)、十六进制值、RGB、开尔文、色调亮度和饱和度。这一点很重要,因为它会影响到这个项目中最难的部分:将温度转换为颜色。
用颜色表示温度
如果你曾经看过天气预报,你就会熟悉气象学在天气图上用颜色表示天气状况的方式。

通常,这样做是为了可视化降水。你可能在天气图上见过那条不祥的绿色风暴带正向你袭来,而你则试图弄清楚是否应该躲进浴缸,因为你正处于龙卷风的路径上。或者也许这只是我们这些身处 美国龙卷风走廊 的不幸灵魂。
颜色也用于表示温度。这正是我想用灯泡做的。困难的是,似乎没有标准化的方式来做到这一点。有些地图将其显示为带状的纯色。在这种情况下,蓝色可能表示 0℉ – 32℉ 的范围。

其他地图则将其显示为渐变尺度,这更精确。这正是我想要的天气灯泡的效果。

我第一次尝试解决这个问题是简单地谷歌搜索“温度颜色尺度”以及该搜索词的其他各种变体。我得到了很多关于开尔文的信息。
开尔文是颜色温度的表示。从字面上看。对于任何光源(灯泡、太阳等),光源的实际温度都会影响其发出的光的颜色。火焰燃烧发出黄红色的光。火焰越热,颜色就越接近白色。因此有句谚语,“白热”。所以如果有人说“红热”,你可以在所有人面前纠正他们,因为谁不喜欢一个喜欢抬杠的人呢?
LIFX 灯泡支持开尔文,所以你可能会认为这会奏效。毕竟,这是开尔文温标……

问题在于,颜色变化不足,因为这些不是实际的颜色,而是光源根据其“温度”发出的色彩。以下是 LIFX 应用程序附带的开尔文色轮。

在灯泡上,这些颜色几乎无法区分。这并不是我想要的。
这让我不得不尝试将颜色转换为十六进制、RGB 或其他格式。这很难,因为你从哪里开始?我花了很长时间调整从冷色蓝色(0, 0, 255)
到热色红色(255, 0, 0)
之间的 RGB 比例值。就在这时,我突然意识到也许 HSL 会是一种更好的方法。为什么?因为色相更容易理解。
色相
色相是在 0 到 360 之间的颜色表示。这就是为什么我们经常看到颜色以轮的形式表示(360°)。这是一个过于简单的概括,但除非你想让我开始谈论波长,否则我们就用这个定义吧。
色相色轮如下所示……

如果我们将其展开,就更容易推理了。

我们准备将温度转换为颜色。首先我们需要确定一个温度范围。我选择了 0℉ 到 100℉。我们不能处理无限的温度颜色组合。数字可以无限延伸,颜色却不能。当我们的灯泡始终是亮红色时,温度只能达到 100℉。寒冷也是如此。
如果浅蓝色代表 0℉,我可以在色相尺度的 200 标记处开始。红色将代表 100℉。你可以看到红色位于两个极端,所以我可以向左或向右移动,具体取决于我想用什么颜色来表示温度。它与实际天气程序中使用的颜色并不相同,但谁在乎呢?显然我不在乎。
我选择向右移动,因为左边没有粉色,而粉色是我最喜欢的颜色。我也觉得粉色比绿色更能代表温暖。绿色代表雨和龙卷风。
现在我们可以根据温度反推出色相了。准备好了吗?我们开始吧。
假设外面是清爽的 50℉。
如果 100℉ 是我们所能达到的最高温度(360),而 0℉ 是最低温度(200),那么我们的颜色尺度就有 160 个点。为了弄清楚我们需要在这个 160 个点的范围内移动到哪里,我们可以将当前温度除以 100℉ 的上限,这将给出我们需要在范围内移动的确切百分比,即 50%。如果我们在 160 个点的范围内移动 50%,那么我们就位于 80 处。由于我们从 200 开始,因此我们的色相为 280。
这听起来很复杂,但这仅仅是因为数学中的文字题很**糟糕**。以下是代码最终的样子……
let hue = 200 + (160 * ( temperature / 100 ));
好的!我们根据色相得到了一个动态颜色尺度,你可能不知道,我们可以像传递命名颜色一样简单地将色相传递给 LIFX。

现在我们只需要找出当前温度是多少,反推出色相,并每隔几分钟执行一次。Serverless,我们来了!
Serverless 定时器函数
Serverless 风靡一时。它就像曾经的 HTML5 一样:它是什么并不重要,重要的是你是否知道这个词并且不害怕在博客文章中使用它。
在此示例中,我们将使用 Azure Functions,因为它支持定时器触发器,并且我们可以在使用 VS Code 部署之前在本地测试这些定时器触发器。Serverless 中让我感到恼火的一件事是,当我无法 在本地调试它 时。
使用 Azure Functions Core Tools 和 Azure Functions VS Code 扩展,我可以创建一个新的 Serverless 项目并选择一个定时器触发器。

Azure Functions 中的定时器触发器被指定为 Cron 表达式。别担心,我也不知道那是什么。
Cron 表达式允许你非常精确地定义间隔。Cron 将时间分解为秒、分、时、日、月、年。因此,如果你想每秒、每分钟、每小时、每天、每月、每年运行一次,你的表达式将如下所示……
* * * * * *
如果你想每天 10:15 运行一次,则表达式将如下所示……
* 15 10 * * *
如果你想每 5 分钟运行一次(这是 Azure 的默认设置),则可以通过说“当分钟数可以被 5 整除时”来指定。
0 */5 * * * *
出于此函数的目的,我们将其设置为 2 分钟。

我使用 2 分钟的间隔,因为这是我们可以免费调用天气 API 的频率 💰。
从 DarkSky 获取天气预报
DarkSky 有一个很棒的天气 API,你可以免费每天调用 1000 次。如果一天有 1440 分钟(确实有),这意味着我们可以每天每 1.44 分钟调用一次 DarkSky 并保持在免费范围内。我只是四舍五入到 2 分钟,因为温度变化没有那么快。
当我们调用 DarkSky API 时,我们的函数是这样的。我的所有令牌、密钥、纬度和经度设置都在环境变量中,因此它们没有硬编码。这些设置在 local.settings.json
文件中。我使用 axios
进行 HTTP 请求,因为它是一个神奇、神奇的包。
const axios = require('axios');
module.exports = function (context, myTimer) {
// build up the DarkSky endpoint
let endpoint = `${process.env.DS_API}/${process.env.DS_SECRET}/${process.env.LAT},
${process.env.LNG}`;
// use axios to call DarkSky for weather
axios
.get(endpoint)
.then(response => {
let temp = Math.round(response.data.currently.temperature);
// TODO: Set the color of the LIFX bulb
})
.catch(err => {
context.log(err.message);
});
};
现在我有了温度,我需要调用 LIFX API。你猜怎么着,有人已经创建了一个名为 lifx-http-api
的 npm 包来执行此操作。这就是你热爱 JavaScript 的原因。
设置灯泡色调
天气结果返回后,我需要使用 LIFX API 实例并调用 setState
方法。此方法返回一个 promise,这意味着我们需要嵌套 promise。嵌套 promise 会变得难以控制,可能会让我们回到回调地狱,这正是我们最初使用 promise 想要避免的事情。
相反,我们将处理第一个 promise,然后返回 Promise.all
,我们可以在另一个顶层的 then
中处理它。这只是防止我们嵌套 then
语句。
记住孩子们,promise 只是在社会上可以接受的回调。
const axios = require('axios');
const LIFX = require('lifx-http-api');
let client = new LIFX({
bearerToken: process.env.LIFX_TOKEN
});
module.exports = function (context, myTimer) {
// build up the DarkSky endpoint
let endpoint = <code>${process.env.DS_API}/${process.env.DS_SECRET}/${
process.env.LAT
},${process.env.LNG}<code>;
// use axios to call DarkSky for weather
axios
.get(endpoint)
.then(response => {
let temp = Math.round(response.data.currently.temperature);
// make sure the temp isn't above 100 because that's as high as we can go
temp = temp < 100 ? temp : 100;
// determine the hue
let hue = 200 + (160 * (temp / 100));
// return Promise.all so we can resolve at the top level
return Promise.all([
data,
client.setState('all', { color: <code>hue:${hue}<code> })
]);
})
.then(result => {
// result[0] contains the darksky result
// result[1] contains the LIFX result
context.log(result[1]);
})
.catch(err => {
context.log(err.message);
});
};
现在我们可以本地运行这个东西,并观察我们的计时器执行它的操作。

就是这样!让我们部署它。
部署天气灯泡
我可以从 VS Code 扩展创建新的 Functions 项目。

我可以右键单击它以“在门户中打开”,在那里我可以定义部署源,以便它从 Github 中提取我的代码并进行部署。这是理想的选择,因为现在每当我将更改推送到 Github 时,我的应用程序都会自动重新部署。

万岁,天气灯泡!
现在只需坐下来欣赏天气灯泡的柔和光芒!为什么要查看实际温度,当你可以查看这种美丽的热粉色时?
你能根据本文中的知识猜测温度是多少吗?发表评论并猜得最接近的人将获得我赠送的免费 LIFX 灯泡(因为我❤️你们所有人),或者如果您在 美国以外,则支付灯泡费用(约 40 美元)。

您可以从Github获取此项目的所有代码。
63°F?
我猜温度在华氏 88 度左右?
精彩的文章!
你在解决项目挑战的过程中表现出的热情让这篇文章读起来很有趣。通篇写得都很好。
在你的引言中,你半开玩笑地写道,你正在解决一个第一世界的问题。但我认为这又是可访问性的一个例子。一个人不需要识字或了解任何温标,他或她仍然可以通过颜色来感知外部环境。我认为这除了其最初目的之外,还可以有一些有用的应用。
哦,我猜是 62 华氏度。 :)
我认为你提出了一个很好的观点。我的孩子们每天早上上学前都会进来看看灯泡。他们知道如何阅读温度,但他们还太小,无法理解 50 度是冷还是暖。如果灯泡是蓝色的,他们会穿上夹克。
猜得很好! :)
让我们看看,根据油罐顶部反射的光的颜色,rgb(250,85,230) = 色调 307
色调 = 200 + (160 * (温度 / 100))
或(进行代数运算)
温度 = (色调 – 200) / 1.6
温度 = 67ºF
获胜者
很棒的文章!这是一个非常有趣且有见地的实验,让我感到很兴奋。我猜最后那个深粉色大约是 66-70 度。
我想说你的灯泡大约是 69 度,考虑到你在美国中西部,这是一个美好的春日。
田纳西州纳什维尔算中西部吗?我以为我们是南方。而且要被认为是中西部,必须有很多玉米地吗?我们主要是大豆。还有烟草。大豆和烟草。还有水晶汉堡。
猜得很好 :)
85°F? :)
很棒的文章。也让我想要尝试一下。我喜欢谜题,所以我的答案是 62 度。
我使用了油罐最上面白色油漆部分反射的光,调整了白平衡,并对颜色进行平均以找到色调 299。使用你上面提到的数学方法((299 – 200) / 160 * 100),我得到了 61.875。
“A”表示努力? :)
A ✅
很棒的文章!这是一个非常有趣的想法,一篇有创意的文章,也让我想要在我的家里也设置一个。(我可以看到将其他天气数据(如湿度、降水和风寒)结合起来,以确定一个红色到绿色或红色到白色的色调值,来描述天气的宜人程度。)
也许我可以赢得一个 LIFX 灯泡并尝试一下,猜测你的灯的照片中温度是 64 度?
这是一个很棒的想法。我曾想过尝试用灯泡来显示未来一小时内的降水概率。如果很快要下雨,也许可以每隔一段时间闪烁一下绿色。
猜得很好 :)
非常酷的项目!我猜一下温度……75°F
我的温度猜测:华氏 91 度!
优秀的文章!让我一直笑个不停,也激励我购买其中一个并在我的办公桌上设置一个!
91 – 我的朋友,你绝对准备好迎接夏天了!猜得很好,但太高了。不过我喜欢你的想法。用 LIFX Mini Color 灯泡奖励你自己的乐观精神。它们玩起来很有趣。
我想说 78 度!
很棒的文章——感谢分享。 :)
66 华氏度
68 度!
78
非常有趣的文章!我估计大约是65度。如果我将来有机会自己实现它,我必须尝试将风寒/湿度也考虑进去。我有一个投影时钟显示室外温度,但当我早上出门时,这并不能说明全部情况。
看起来对我来说是73ºF。
我们有赢家了!
大家回到主题帖并祝贺Mark H。他得到了正确的答案(67)并且他做了数学计算。
感谢大家精彩的猜测和友好的评论。
恭喜Mark - 你的LIFX正在路上。
向天气灯球致敬! ⛅️
啊,伙计!几个月前我画了一个方案来做这个,现在我看到了你的帖子,做得太棒了。我想我还是要做,但你已经让它变得更容易了!做得很好,伙计!
很棒的文章。我正在使用这样的项目来帮助我学习。你在哪里获取了要放入Postman中的LIFX API连接信息?我在应用程序中注册了我的灯泡,但没有收到任何回复,也找不到任何连接到API的设置。
让我澄清一下,我找到了API文档,但我不确定你是如何绕过OAuth以及如何在Postman中进行身份验证的。谢谢!