[source/keys:feat] Nest Thermostat bindings
This commit is contained in:
parent
bd726a168a
commit
6c5e38f480
86
actions.js
86
actions.js
|
@ -704,4 +704,90 @@ actions.wp.toggleSimple = () => {
|
|||
actions.openLink(u.href)()
|
||||
}
|
||||
|
||||
// Nest Thermostat Controller
|
||||
// --------------------------
|
||||
actions.nt = {}
|
||||
actions.nt.adjustTemp = (dir) => () =>
|
||||
document.querySelector(
|
||||
`button[data-test='thermozilla-controller-controls-${dir > 0 ? "in" : "de"}crement-button']`,
|
||||
).click()
|
||||
|
||||
actions.nt.setMode = (mode) => async () => {
|
||||
const selectMode = async (popover) => {
|
||||
const query = () => !popover.isConnected
|
||||
const q = query()
|
||||
if (q) return q
|
||||
popover.querySelector(`button[data-test='thermozilla-mode-switcher-${mode}-button']`).click()
|
||||
return util.until(query)
|
||||
}
|
||||
|
||||
const openPopover = async () => {
|
||||
const query = () => document.querySelector("div[data-test='thermozilla-mode-popover']")
|
||||
const q = query()
|
||||
if (q) return q
|
||||
document.querySelector("button[data-test='thermozilla-mode-button']").click()
|
||||
return util.until(query)
|
||||
}
|
||||
|
||||
const popover = await openPopover()
|
||||
return selectMode(popover)
|
||||
}
|
||||
|
||||
actions.nt.setFan = (desiredState) => async () => {
|
||||
const startStopFan = async (startStop, popover) => {
|
||||
const query = () => !popover.isConnected
|
||||
const q = query()
|
||||
if (q) return q
|
||||
popover.querySelector(`div[data-test='thermozilla-fan-timer-${startStop}-button']`).click()
|
||||
return util.until(query)
|
||||
}
|
||||
|
||||
const selectFanTime = async (popover, listbox) => {
|
||||
const query = () => !listbox.isConnected
|
||||
const q = query()
|
||||
if (q) return q
|
||||
Hints.dispatchMouseClick(listbox.querySelector("div[role='option']:last-child"))
|
||||
return util.until(query)
|
||||
}
|
||||
|
||||
const openFanListbox = async (popover) => {
|
||||
const query = () => popover.querySelector("div[role='listbox']")
|
||||
const q = query()
|
||||
if (q) return q
|
||||
Hints.dispatchMouseClick(popover.querySelector("div[role='combobox']"))
|
||||
return util.until(query)
|
||||
}
|
||||
|
||||
const openPopover = async () => {
|
||||
const query = () => document.querySelector("div[data-test='thermozilla-fan-timer-popover']")
|
||||
const q = query()
|
||||
if (q) return q
|
||||
document.querySelector("button[data-test='thermozilla-fan-button']").click()
|
||||
return util.until(query)
|
||||
}
|
||||
|
||||
const fanRunning = () => document.querySelector("div[data-test='thermozilla-aag-fan-listcell-title']")
|
||||
|
||||
const startFan = async () => {
|
||||
const popover = await openPopover()
|
||||
const listbox = await openFanListbox(popover)
|
||||
await selectFanTime(popover, listbox)
|
||||
return startStopFan("start", popover)
|
||||
}
|
||||
|
||||
const stopFan = async () => {
|
||||
const popover = await openPopover()
|
||||
await startStopFan("stop", popover)
|
||||
await util.until(() => !fanRunning())
|
||||
}
|
||||
|
||||
if (fanRunning()) {
|
||||
await stopFan()
|
||||
}
|
||||
|
||||
if (desiredState === 1) {
|
||||
await startFan()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = actions
|
||||
|
|
53
keys.js
53
keys.js
|
@ -758,6 +758,59 @@ const maps = {
|
|||
callback: actions.createHint("a[href^='/packages/'][href$='/']"),
|
||||
},
|
||||
],
|
||||
|
||||
"home.nest.com": [
|
||||
{
|
||||
path: "/thermostat/DEVICE_.*",
|
||||
leader: "",
|
||||
alias: ["+", "="],
|
||||
description: "Increment temperature",
|
||||
callback: actions.nt.adjustTemp(1),
|
||||
},
|
||||
{
|
||||
path: "/thermostat/DEVICE_.*",
|
||||
leader: "",
|
||||
alias: ["-", "_"],
|
||||
description: "Decrement temperature",
|
||||
callback: actions.nt.adjustTemp(-1),
|
||||
},
|
||||
{
|
||||
path: "/thermostat/DEVICE_.*",
|
||||
alias: "h",
|
||||
description: "Switch mode to Heat",
|
||||
callback: actions.nt.setMode("heat"),
|
||||
},
|
||||
{
|
||||
path: "/thermostat/DEVICE_.*",
|
||||
alias: "c",
|
||||
description: "Switch mode to Cool",
|
||||
callback: actions.nt.setMode("cool"),
|
||||
},
|
||||
{
|
||||
path: "/thermostat/DEVICE_.*",
|
||||
alias: "r",
|
||||
description: "Switch mode to Heat/Cool",
|
||||
callback: actions.nt.setMode("range"),
|
||||
},
|
||||
{
|
||||
path: "/thermostat/DEVICE_.*",
|
||||
alias: "o",
|
||||
description: "Switch mode to Off",
|
||||
callback: actions.nt.setMode("off"),
|
||||
},
|
||||
{
|
||||
path: "/thermostat/DEVICE_.*",
|
||||
alias: "f",
|
||||
description: "Switch fan On",
|
||||
callback: actions.nt.setFan(1),
|
||||
},
|
||||
{
|
||||
path: "/thermostat/DEVICE_.*",
|
||||
alias: "F",
|
||||
description: "Switch fan Off",
|
||||
callback: actions.nt.setFan(0),
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
// Aliases
|
||||
|
|
38
util.js
38
util.js
|
@ -9,7 +9,8 @@ util.getCurrentLocation = (prop = "href") => {
|
|||
return window.location[prop]
|
||||
}
|
||||
|
||||
util.escape = (str) => String(str).replace(/[&<>"'`=/]/g, (s) => ({
|
||||
util.escape = (str) =>
|
||||
String(str).replace(/[&<>"'`=/]/g, (s) => ({
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
|
@ -20,6 +21,27 @@ util.escape = (str) => String(str).replace(/[&<>"'`=/]/g, (s) => ({
|
|||
"=": "=",
|
||||
}[s]))
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
|
||||
util.escapeRegExp = (str) =>
|
||||
str.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&")
|
||||
|
||||
util.until = (check, test = (a) => a, maxAttempts = 50, interval = 50) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const f = (attempts = 0) => {
|
||||
const res = check()
|
||||
if (!test(res)) {
|
||||
if (attempts > maxAttempts) {
|
||||
reject(new Error("until: timeout"))
|
||||
} else {
|
||||
setTimeout(() => f(attempts + 1), interval)
|
||||
}
|
||||
return
|
||||
}
|
||||
resolve(res)
|
||||
}
|
||||
f()
|
||||
})
|
||||
|
||||
util.createSuggestionItem = (html, props = {}) => {
|
||||
const li = document.createElement("li")
|
||||
li.innerHTML = html
|
||||
|
@ -98,24 +120,26 @@ util.processMaps = (maps, aliases, siteleader) => {
|
|||
leader = (domain === "global") ? "" : siteleader,
|
||||
category = categories.misc,
|
||||
description = "",
|
||||
} = mapObj
|
||||
path = "(/.*)?",
|
||||
} = mapObj;
|
||||
(Array.isArray(alias) ? alias : [alias]).forEach((a) => {
|
||||
const opts = {}
|
||||
|
||||
const key = `${leader}${alias}`
|
||||
const key = `${leader}${a}`
|
||||
|
||||
// Determine if it's a site-specific mapping
|
||||
if (domain !== "global") {
|
||||
const d = domain.replace(".", "\\.")
|
||||
opts.domain = new RegExp(`^http(s)?://(([a-zA-Z0-9-_]+\\.)*)(${d})(/.*)?`)
|
||||
const d = util.escapeRegExp(domain)
|
||||
opts.domain = new RegExp(`^http(s)?://(([a-zA-Z0-9-_]+\\.)*)(${d})${path}`)
|
||||
}
|
||||
|
||||
const fullDescription = `#${category} ${description}`
|
||||
|
||||
if (mapObj.map !== undefined) {
|
||||
map(alias, mapObj.map)
|
||||
map(a, mapObj.map)
|
||||
} else {
|
||||
mapkey(key, fullDescription, callback, opts)
|
||||
}
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue