/**
* Questmate Custom Component - Philips Hue Remote Control (Questscript)
*
* Permissions:
* @UseApp {HUE}
*
* Changelog:
* v0.1 Initial release
* v0.2 Converted Custom V2 component
* v0.3 Added support for scenes and selecting multiple lights instead of just one
*
*/
return defineCustomItem((Questmate) => {
Questmate.registerView("ITEM_RUN_VIEW", async ({ useConfigData }) => {
const [allowedScenes] = useConfigData("allowedScenes", []);
const [allowedLights] = useConfigData("allowedLights", []);
const availableLights = await lightsDataSource.retrieve();
const availableScenes = await scenesDataSource.retrieve();
return {
components:
availableLights.length === 0 && availableScenes.length === 0
? [
{
id: "loading-text",
type: "text",
content: " ",
},
]
: [
...availableLights
.filter((light) => allowedLights.includes(light.id))
.map((light) => {
return {
id: `light-${light.id}`,
type: "switch",
title: `💡 ${light.name}`,
value: light.data.on,
onSwitch: async (switchOn) => {
const lightResponse = await fetch(
`https://api.meethue.com/route/clip/v2/resource/light/${light.id}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ on: { on: switchOn } }),
}
);
const lightResponseData = await lightResponse.json();
console.log(
"lightResponseData",
JSON.stringify(lightResponseData, null, 2)
);
lightsDataSource.markResultsAsStale();
},
};
}),
...availableScenes
.filter((scene) => allowedScenes.includes(scene.id))
.map((scene) => ({
id: `scene-${scene.id}`,
type: "button",
title: `🖼️ ${scene.name}`,
buttonLabel: "Activate",
onPress: async () => {
const sceneResponse = await fetch(
`https://api.meethue.com/route/clip/v2/resource/scene/${scene.id}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
recall: {
action: "active",
},
}),
}
);
const sceneResponseData = await sceneResponse.json();
console.log(
"sceneResponseData",
JSON.stringify(sceneResponseData, null, 2)
);
},
})),
],
};
});
Questmate.registerView("ITEM_CONFIG_VIEW", async ({ useConfigData }) => {
const [allowedScenes, setAllowedScenes] = useConfigData(
"allowedScenes",
[]
);
const [allowedLights, setAllowedLights] = useConfigData(
"allowedLights",
[]
);
const availableScenes = await scenesDataSource.retrieve();
const availableLights = await lightsDataSource.retrieve();
return {
components: [
{
id: "allowedLights",
type: "text",
content: "Allowed Lights",
},
...availableLights.map((light) => ({
id: `light-${light.id}`,
type: "switch",
title: light.name,
value: allowedLights.includes(light.id),
onSwitch: async (switchOn) => {
if (switchOn) {
setAllowedLights([...allowedLights, light.id]);
} else {
setAllowedLights(
allowedLights.filter(
(allowedLight) => allowedLight !== light.id
)
);
}
},
})),
{
id: "allowedScenes",
type: "text",
content: "Allowed Scenes",
},
...availableScenes.map((scene) => ({
id: `scene-${scene.id}`,
type: "switch",
title: scene.name,
value: allowedScenes.includes(scene.id),
onSwitch: async (switchOn) => {
if (switchOn) {
setAllowedScenes([...allowedScenes, scene.id]);
} else {
setAllowedScenes(
allowedScenes.filter(
(allowedScene) => allowedScene !== scene.id
)
);
}
},
})),
],
};
});
const scenesDataSource = Questmate.registerDataSource({
id: "scenes",
initialData: [],
refreshInterval: 120,
fetcher: () => async () => {
const sceneListResponse = await fetch(
"https://api.meethue.com/route/clip/v2/resource/scene"
);
const { data: scenesData } = await sceneListResponse.json();
const scenes = scenesData.map((scene) => ({
name: scene.metadata.name,
id: scene.id,
}));
return {
data: scenes,
};
},
});
const lightsDataSource = Questmate.registerDataSource({
id: "lights",
initialData: [],
refreshInterval: 120,
aggregate: ({ results, staleResults }) => {
const latestFreshResult = results[results.length - 1];
if (latestFreshResult) {
return latestFreshResult;
} else {
return staleResults[staleResults.length - 1];
}
},
fetcher: () => async () => {
const lightListResponse = await fetch(
"https://api.meethue.com/route/clip/v2/resource/light"
);
const { data: lightsData } = await lightListResponse.json();
const lights = lightsData.map((light) => ({
name: light.metadata.name,
id: light.id,
data: {
on: light.on.on,
},
}));
return {
data: lights,
};
},
});
});