homebridge-ir-amplifier
Version:
Homebridge plugin for IR amplifier control with OCR volume detection and CEC support for Apple TV integration
185 lines • 6.76 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OCRController = void 0;
const tesseract_js_1 = require("tesseract.js");
const axios_1 = __importDefault(require("axios"));
// @ts-ignore
const cron = __importStar(require("node-cron"));
class OCRController {
constructor(log, config) {
this.log = log;
this.config = config;
this.isInitialized = false;
if (this.config.ocr?.enabled) {
this.initializeWorker();
}
else {
this.log.info('OCR disabled in configuration');
}
}
async initializeWorker() {
try {
this.worker = await (0, tesseract_js_1.createWorker)({
logger: (m) => {
if (m.status === 'recognizing text') {
this.log.debug('OCR progress:', Math.round(m.progress * 100) + '%');
}
}
});
await this.worker.loadLanguage('eng');
await this.worker.initialize('eng');
await this.worker.setParameters({
tessedit_char_whitelist: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz: -',
tessedit_pageseg_mode: '8', // Single word
});
this.isInitialized = true;
this.log.info('OCR worker initialized');
}
catch (error) {
this.log.error('Failed to initialize OCR worker:', error);
// Continue without OCR if it fails
this.isInitialized = false;
}
}
async captureScreen() {
try {
const response = await axios_1.default.get(this.config.ocr.cameraUrl, {
responseType: 'arraybuffer',
timeout: 5000,
});
return Buffer.from(response.data);
}
catch (error) {
this.log.error('Failed to capture screen:', error);
return null;
}
}
async extractVolumeFromText(text) {
// Look for volume patterns like "VOL: 45", "Volume 45", "45%", etc.
const volumePatterns = [
/vol[ume]*\s*:?\s*(\d+)/i,
/(\d+)\s*%/,
/volume\s*(\d+)/i,
];
for (const pattern of volumePatterns) {
const match = text.match(pattern);
if (match) {
const volume = parseInt(match[1], 10);
if (volume >= 0 && volume <= 100) {
return volume;
}
}
}
return null;
}
async extractSourceFromText(text) {
// Look for source patterns
const sourcePatterns = [
/source\s*:?\s*([a-zA-Z0-9\s]+)/i,
/input\s*:?\s*([a-zA-Z0-9\s]+)/i,
/video\s*(\d+)/i,
];
for (const pattern of sourcePatterns) {
const match = text.match(pattern);
if (match) {
return match[1].trim();
}
}
return null;
}
async processImage(imageBuffer) {
if (!this.isInitialized || !this.worker) {
this.log.error('OCR worker not initialized');
return { volume: null, source: null, confidence: 0 };
}
try {
const { data: { text, confidence } } = await this.worker.recognize(imageBuffer);
this.log.debug('OCR Text:', text);
this.log.debug('OCR Confidence:', confidence);
const volume = await this.extractVolumeFromText(text);
const source = await this.extractSourceFromText(text);
return {
volume,
source,
confidence: confidence / 100, // Convert to 0-1 scale
};
}
catch (error) {
this.log.error('OCR processing failed:', error);
return { volume: null, source: null, confidence: 0 };
}
}
async getVolumeAndSource() {
if (!this.config.ocr?.enabled) {
return { volume: null, source: null, confidence: 0 };
}
const imageBuffer = await this.captureScreen();
if (!imageBuffer) {
return { volume: null, source: null, confidence: 0 };
}
return this.processImage(imageBuffer);
}
// Method to start periodic OCR checking
startPeriodicCheck(callback) {
if (!this.config.ocr?.enabled) {
this.log.info('OCR periodic check disabled');
return;
}
const interval = this.config.ocr.checkInterval || 30000; // Default 30 seconds
cron.schedule(`*/${Math.floor(interval / 1000)} * * * * *`, async () => {
try {
const result = await this.getVolumeAndSource();
if (result.volume !== null || result.source !== null) {
callback(result);
}
}
catch (error) {
this.log.error('Error in periodic OCR check:', error);
}
});
}
async terminate() {
if (this.worker) {
await this.worker.terminate();
this.log.info('OCR worker terminated');
}
}
}
exports.OCRController = OCRController;
//# sourceMappingURL=ocrController.js.map