homebridge-virtual-accessories
Version:
Virtual HomeKit accessories for Homebridge.
283 lines • 14.4 kB
JavaScript
/* eslint-disable brace-style */
import { BinarySensor } from '../binarySensor.js';
import { Trigger } from './trigger.js';
import { shutdownSignal, Utils } from '../../utils/utils.js';
import * as cheerio from 'cheerio';
/**
* IkeaMatterStockTrigger - Trigger implementation
*/
export class IkeaMatterStockTrigger extends Trigger {
easyrebuildURL = (countryCode, itemNumber) => `https://easyrebuild.com/stock?country=${countryCode}&itemNo=${itemNumber}`;
count = 0;
constructor(sensor, name) {
super(sensor, name);
const triggerConfig = this.sensorConfig.ikeaMatterStockTrigger;
if (triggerConfig.isDisabled) {
this.log.info(`[${this.accessoryName}] Ikea Matter Stock trigger is disabled`);
return;
}
this.start(this, triggerConfig);
}
async start(trigger, triggerConfig) {
await Utils.delay(10000, trigger.accessoryName, trigger.log); // 10 second delay
trigger.checkStockAvailability(trigger, triggerConfig, true);
const intervalBetweenChecksMillis = 60 * 60 * 1000; // trigger.intervalBetweenChecksMillis: 60 minutes
setInterval(trigger.checkStockAvailability, intervalBetweenChecksMillis, trigger, triggerConfig, false)
.unref();
}
/**
* Private methods
*/
async checkStockAvailability(trigger, triggerConfig, firstRun) {
if (shutdownSignal.isShuttingDown) {
return;
}
const countryCode = trigger.getCountryCode(triggerConfig.country);
if (countryCode === undefined) {
trigger.log.error(`[${trigger.accessoryName}] Country not supported: ${triggerConfig.country}`);
return;
}
trigger.log.debug(`[${trigger.accessoryName}] Country code: ${triggerConfig.country} - ${countryCode}`);
const itemInfo = trigger.getItemInfo(countryCode, triggerConfig.itemName);
if (itemInfo === undefined) {
trigger.log.error(`[${trigger.accessoryName}] Item not supported: ${triggerConfig.itemName}`);
return;
}
const itemCode = itemInfo[0];
const displayName = itemInfo[1];
if (itemCode === '') {
trigger.log.error(`[${trigger.accessoryName}] Unknown item code for: ${displayName} in ${triggerConfig.country}`);
return;
}
trigger.log.debug(`[${trigger.accessoryName}] Item code: ${displayName} - ${itemCode}`);
const html = await trigger.getHtml(countryCode, itemCode, trigger);
if (html === undefined) {
return;
}
trigger.log.debug(`[${trigger.accessoryName}] Retrieved Ikea matter stock data`);
const dom = cheerio.load(html);
let foundLocation = false;
const selector = 'table.table tbody tr.expandable-row';
dom(`${selector}`).each((_, row) => {
const cells = dom(row).find('td');
if (cells.length < 2) {
// Next row
return;
}
const store = dom(cells[0]).text().replace(/\s+/g, ' ').trim();
const count = dom(cells[1]).text().replace(/\s+/g, ' ').trim();
if (store.toLowerCase().startsWith('ikea') && store.toLowerCase().endsWith(triggerConfig.storeLocation.toLowerCase())) {
foundLocation = true;
const countNum = Number.parseInt(count);
if (Number.isNaN(countNum)) {
trigger.log.error(`[${trigger.accessoryName}] Error in Ikea matter stock data. Count is not a number`);
}
else if (countNum > 0) {
if (trigger.sensor.getSensorState() !== BinarySensor.TRIGGERED || firstRun === true || countNum !== trigger.count) {
trigger.log.info(`[${trigger.accessoryName}] Ikea matter stock data shows a count of ${countNum} ${displayName} in ${triggerConfig.storeLocation}`);
trigger.sensor.triggerSensorState(BinarySensor.TRIGGERED, trigger);
trigger.count = countNum;
}
else {
// eslint-disable-next-line max-len
trigger.log.debug(`[${trigger.accessoryName}] Ikea matter stock data shows a count of ${countNum} ${displayName} in ${triggerConfig.storeLocation}`);
}
}
else {
if (trigger.sensor.getSensorState() !== BinarySensor.NORMAL || firstRun === true) {
trigger.log.info(`[${trigger.accessoryName}] Ikea matter stock data shows a count of ${countNum} ${displayName} in ${triggerConfig.storeLocation}`);
trigger.sensor.triggerSensorState(BinarySensor.NORMAL, trigger);
}
else {
// eslint-disable-next-line max-len
trigger.log.debug(`[${trigger.accessoryName}] Ikea matter stock data shows a count of ${countNum} ${displayName} in ${triggerConfig.storeLocation}`);
}
}
return false; // breaks out of the loop
}
});
if (foundLocation === false) {
trigger.log.info(`[${trigger.accessoryName}] Location ${triggerConfig.storeLocation} not found in ${triggerConfig.country}`);
}
}
async getHtml(countryCode, itemCode, trigger) {
const productCodes = [itemCode, itemCode.replaceAll('.', '')];
let validProductNumber = true;
const errorMessages = [];
for (const productCode of productCodes) {
const request = new Request(trigger.easyrebuildURL(countryCode, productCode), { method: 'GET' });
trigger.log.debug(`[${trigger.accessoryName}] Requesting Ikea matter stock data from: ${(request.url)}`);
let htmlFetchResponse;
try {
htmlFetchResponse = await fetch(request);
}
catch (error) {
errorMessages.push(`Failed retrieving Ikea matter stock data: ${JSON.stringify(error)}`);
continue;
}
if (htmlFetchResponse === undefined || !htmlFetchResponse.ok) {
errorMessages.push(`Error retrieving Ikea matter stock data. Response status: ${htmlFetchResponse?.status}`);
continue;
}
const html = await htmlFetchResponse.text();
if (html === undefined) {
errorMessages.push('Response did not return html');
continue;
}
else if (html.includes('is not a valid product number')) {
validProductNumber = false;
// Try the next format
continue;
}
return html;
}
if (validProductNumber !== true) {
const itemName = trigger.sensorConfig.ikeaMatterStockTrigger.itemName;
trigger.log.error(`[${trigger.accessoryName}] Not a valid product number for ${itemName}: "${productCodes[0]}" / "${productCodes[1]}"`);
}
else {
for (const errorMsg of errorMessages) {
trigger.log.error(`[${trigger.accessoryName}] ${errorMsg}`);
}
}
return undefined;
}
getCountryCode(countryName) {
const countryCodes = new Map([
// ***** North America *****
['USA', 'us'],
['Canada', 'ca'],
// ***** Europe *****
['UK', 'gb'],
// ***** Asia *****
['Japan', 'jp'],
['Korea', 'kr'],
// ***** Oceania *****
['Australia', 'au'],
['New Zealand', 'nz'],
]);
return countryCodes.get(countryName);
}
getItemInfo(countryCode, itemName) {
const countryCodes = new Map([
// ***** North America *****
[
// USA
'us', new Map([
['ALPSTUGA', ['706.093.96', 'ALPSTUGA']],
['BILRESA', ['806.178.76', 'BILRESA']],
['GRILLPLATS', ['706.247.40', 'GRILLPLATS']],
['KLIPPBOK', ['506.177.69', 'KLIPPBOK']],
['MYGGBETT', ['606.176.41', 'MYGGBETT']],
['MYGGSPRAY', ['806.194.51', 'MYGGSPRAY']],
['TIMMERFLOTTE', ['506.189.57', 'TIMMERFLOTTE']],
['KAJPLATS-COLOR-M', ['306.114.62', 'KAJPLATS E26 Color 1100lm']],
['KAJPLATS-WHITE-S', ['606.113.09', 'KAJPLATS E26 White 450lm']],
['KAJPLATS-WHITE-M', ['506.113.00', 'KAJPLATS E26 White 1100lm']],
['KAJPLATS-WHITE-L', ['406.113.05', 'KAJPLATS E26 White 1600lm']],
]),
],
[
// Canada
'ca', new Map([
['ALPSTUGA', ['706.093.96', 'ALPSTUGA']],
['BILRESA', ['806.178.76', 'BILRESA']],
['GRILLPLATS', ['706.247.40', 'GRILLPLATS']],
['KLIPPBOK', ['506.177.69', 'KLIPPBOK']],
['MYGGBETT', ['606.176.41', 'MYGGBETT']],
['MYGGSPRAY', ['806.194.51', 'MYGGSPRAY']],
['TIMMERFLOTTE', ['506.189.57', 'TIMMERFLOTTE']],
['KAJPLATS-COLOR-M', ['106.114.63', 'KAJPLATS E26 Color 1100lm']],
['KAJPLATS-WHITE-S', ['606.113.09', 'KAJPLATS E26 White 450lm']],
['KAJPLATS-WHITE-M', ['506.113.00', 'KAJPLATS E26 White 1100lm']],
['KAJPLATS-WHITE-L', ['406.113.05', 'KAJPLATS E26 White 1600lm']],
]),
],
// ***** Europe *****
[
// UK
'gb', new Map([
['ALPSTUGA', ['506.041.87', 'ALPSTUGA']],
['BILRESA', ['706.178.72', 'BILRESA']],
['GRILLPLATS', ['606.247.45', 'GRILLPLATS']],
['KLIPPBOK', ['906.246.40', 'KLIPPBOK']],
['MYGGBETT', ['006.247.05', 'MYGGBETT']],
['MYGGSPRAY', ['306.246.95', 'MYGGSPRAY']],
['TIMMERFLOTTE', ['606.189.52', 'TIMMERFLOTTE']],
['KAJPLATS-COLOR-M', ['506.114.61', 'KAJPLATS E27 Color 1055lm']],
['KAJPLATS-WHITE-S', ['806.113.08', 'KAJPLATS E27 White 470lm']],
['KAJPLATS-WHITE-M', ['106.112.98', 'KAJPLATS E27 White 1055lm']],
['KAJPLATS-WHITE-L', ['906.113.03', 'KAJPLATS E27 White 1521lm']],
]),
],
// ***** Asia *****
[
// Japan
'jp', new Map([
['ALPSTUGA', ['', 'ALPSTUGA']],
['BILRESA', ['506.178.68', 'BILRESA']],
['GRILLPLATS', ['106.247.43', 'GRILLPLATS']],
['KLIPPBOK', ['706.177.68', 'KLIPPBOK']],
['MYGGBETT', ['406.176.42', 'MYGGBETT']],
['MYGGSPRAY', ['006.194.50', 'MYGGSPRAY']],
['TIMMERFLOTTE', ['', 'TIMMERFLOTTE']],
['KAJPLATS-COLOR-M', ['406.192.74', 'KAJPLATS E26 Color 1160lm']],
['KAJPLATS-WHITE-S', ['506.189.76', 'KAJPLATS E26 White 485lm']],
['KAJPLATS-WHITE-M', ['406.189.86', 'KAJPLATS E26 White 1160lm']],
['KAJPLATS-WHITE-L', ['806.190.12', 'KAJPLATS E26 White 1520lm']],
]),
],
[
// Korea
'kr', new Map([
['ALPSTUGA', ['', 'ALPSTUGA']],
['BILRESA', ['406.415.24', 'BILRESA']],
['GRILLPLATS', ['506.247.41', 'GRILLPLATS']],
['KLIPPBOK', ['706.177.68', 'KLIPPBOK']],
['MYGGBETT', ['406.176.42', 'MYGGBETT']],
['MYGGSPRAY', ['006.194.50', 'MYGGSPRAY']],
['TIMMERFLOTTE', ['', 'TIMMERFLOTTE']],
['KAJPLATS-COLOR-M', ['606.192.73', 'KAJPLATS E26 Color 1055lm']],
['KAJPLATS-WHITE-S', ['306.189.77', 'KAJPLATS E26 White 470lm']],
['KAJPLATS-WHITE-M', ['206.189.87', 'KAJPLATS E26 White 1055lm']],
['KAJPLATS-WHITE-L', ['006.190.11', 'KAJPLATS E26 White 1521lm']],
]),
],
// ***** Oceania *****
[
// Australia
'au', new Map([
['ALPSTUGA', ['706.093.77', 'ALPSTUGA']],
['BILRESA', ['506.178.68', 'BILRESA']],
['GRILLPLATS', ['306.247.42', 'GRILLPLATS']],
['KLIPPBOK', ['706.177.68', 'KLIPPBOK']],
['MYGGBETT', ['406.176.42', 'MYGGBETT']],
['MYGGSPRAY', ['006.194.50', 'MYGGSPRAY']],
['TIMMERFLOTTE', ['006.189.50', 'TIMMERFLOTTE']],
['KAJPLATS-COLOR-M', ['206.192.70', 'KAJPLATS E27 Color 1055lm']],
['KAJPLATS-WHITE-S', ['206.189.68', 'KAJPLATS E27 White 470lm']],
['KAJPLATS-WHITE-M', ['106.189.83', 'KAJPLATS E27 White 1055lm']],
['KAJPLATS-WHITE-L', ['806.190.07', 'KAJPLATS E27 White 1521lm']],
]),
],
[
// New Zealand
'nz', new Map([
['ALPSTUGA', ['706.093.77', 'ALPSTUGA']],
['BILRESA', ['506.178.68', 'BILRESA']],
['GRILLPLATS', ['306.247.42', 'GRILLPLATS']],
['KLIPPBOK', ['706.177.68', 'KLIPPBOK']],
['MYGGBETT', ['406.176.42', 'MYGGBETT']],
['MYGGSPRAY', ['006.194.50', 'MYGGSPRAY']],
['TIMMERFLOTTE', ['006.189.50', 'TIMMERFLOTTE']],
['KAJPLATS-COLOR-M', ['206.192.70', 'KAJPLATS E27 Color 1055lm']],
['KAJPLATS-WHITE-S', ['206.189.68', 'KAJPLATS E27 White 470lm']],
['KAJPLATS-WHITE-M', ['106.189.83', 'KAJPLATS E27 White 1055lm']],
['KAJPLATS-WHITE-L', ['806.190.07', 'KAJPLATS E27 White 1521lm']],
]),
],
]);
return countryCodes.get(countryCode)?.get(itemName);
}
}
//# sourceMappingURL=triggerIkeaMatterStock.js.map