Skip to content

Commit

Permalink
Fixes handling the welcome message status from bticino (#1432)
Browse files Browse the repository at this point in the history
* Fix voicemail status by parsing the VSC more accurately

* Implement video clip streaming by fetching and converting the voicemail message
  • Loading branch information
slyoldfox committed Apr 14, 2024
1 parent 0f4ff0d commit 6dba80c
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 19 deletions.
26 changes: 13 additions & 13 deletions plugins/bticino/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion plugins/bticino/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@scrypted/bticino",
"version": "0.0.13",
"version": "0.0.15",
"scripts": {
"scrypted-setup-project": "scrypted-setup-project",
"prescrypted-setup-project": "scrypted-package-json",
Expand Down
89 changes: 86 additions & 3 deletions plugins/bticino/src/bticino-camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ import { ffmpegLogInitialOutput, safeKillFFmpeg, safePrintFFmpegArguments } from
import { PersistentSipManager } from './persistent-sip-manager';
import { InviteHandler } from './bticino-inviteHandler';
import { SipOptions, SipRequest } from '../../sip/src/sip-manager';
import fs from "fs"
import url from "url"
import path from 'path';
import { default as stream } from 'node:stream'
import type { ReadableStream } from 'node:stream/web'
import { finished } from "stream/promises";

import { get } from 'http'
import { ControllerApi } from './c300x-controller-api';
Expand All @@ -25,6 +31,7 @@ import { BticinoMuteSwitch } from './bticino-mute-switch';

const STREAM_TIMEOUT = 65000;
const { mediaManager } = sdk;
const BTICINO_CLIPS = path.join(process.env.SCRYPTED_PLUGIN_VOLUME, 'bticino-clips');

export class BticinoSipCamera extends ScryptedDeviceBase implements MotionSensor, DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor, HttpRequestHandler, VideoClips, Reboot {

Expand Down Expand Up @@ -147,11 +154,87 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements MotionSensor
});
}

getVideoClip(videoId: string): Promise<MediaObject> {
async getVideoClip(videoId: string): Promise<MediaObject> {
const outputfile = await this.fetchAndConvertVoicemailMessage(videoId);

const fileURLToPath: string = url.pathToFileURL(outputfile).toString()
this.console.log(`Creating mediaObject for url: ${fileURLToPath}`)
return await mediaManager.createMediaObjectFromUrl(fileURLToPath);
}

private async fetchAndConvertVoicemailMessage(videoId: string) {
let c300x = SipHelper.getIntercomIp(this)
const url = `http://${c300x}:8080/voicemail?msg=${videoId}/aswm.avi&raw=true`;
return mediaManager.createMediaObjectFromUrl(url);

const response = await fetch(`http://${c300x}:8080/voicemail?msg=${videoId}/aswm.avi&raw=true`);

const contentLength: number = Number(response.headers.get("Content-Length"));
const lastModified: Date = new Date(response.headers.get("Last-Modified-Time"));

const avifile = `${BTICINO_CLIPS}/${videoId}.avi`;
const outputfile = `${BTICINO_CLIPS}/${videoId}.mp4`;

if (!fs.existsSync(BTICINO_CLIPS)) {
this.console.log(`Creating clips dir at: ${BTICINO_CLIPS}`)
fs.mkdirSync(BTICINO_CLIPS);
}

if (fs.existsSync(avifile)) {
const stat = fs.statSync(avifile);
if (stat.size != contentLength || stat.mtime.getTime() != lastModified.getTime()) {
this.console.log(`Size ${stat.size} != ${contentLength} or time ${stat.mtime.getTime} != ${lastModified.getTime}`)
try {
fs.rmSync(avifile);
} catch (e) { }
try {
fs.rmSync(outputfile);
} catch (e) { }
} else {
this.console.log(`Keeping the cached video at ${avifile}`)
}
}

if (!fs.existsSync(avifile)) {
this.console.log("Starting download.")
await finished(stream.Readable.from(response.body as ReadableStream<Uint8Array>).pipe(fs.createWriteStream(avifile)));
this.console.log("Download finished.")
try {
this.console.log(`Setting mtime to ${lastModified}`)
fs.utimesSync(avifile, lastModified, lastModified);
} catch (e) { }
}

const ffmpegPath = await mediaManager.getFFmpegPath();
const ffmpegArgs = [
'-hide_banner',
'-nostats',
'-y',
'-i', avifile,
outputfile
];

safePrintFFmpegArguments(console, ffmpegArgs);
const cp = child_process.spawn(ffmpegPath, ffmpegArgs, {
stdio: ['pipe', 'pipe', 'pipe', 'pipe'],
});

const p = new Promise((resolveFunc) => {
cp.stdout.on("data", (x) => {
this.console.log(x.toString());
});
cp.stderr.on("data", (x) => {
this.console.error(x.toString());
});
cp.on("exit", (code) => {
resolveFunc(code);
});
});

let returnCode = await p;

this.console.log(`Converted file returned code: ${returnCode}`);
return outputfile;
}

getVideoClipThumbnail(thumbnailId: string): Promise<MediaObject> {
let c300x = SipHelper.getIntercomIp(this)
const url = `http://${c300x}:8080/voicemail?msg=${thumbnailId}/aswm.jpg&raw=true`;
Expand Down
8 changes: 6 additions & 2 deletions plugins/bticino/src/bticino-voicemailHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ export class VoicemailHandler extends SipRequestHandler {
handle(request: SipRequest) {
const lastVoicemailMessageTimestamp : number = Number.parseInt( this.sipCamera.storage.getItem('lastVoicemailMessageTimestamp') ) || -1
const message : string = request.content.toString()
if( message.startsWith('*#8**40*0*0*') || message.startsWith('*#8**40*1*0*') ) {
this.aswmIsEnabled = message.startsWith('*#8**40*1*0*');
let matches : Array<RegExpMatchArray> = [...message.matchAll(/\*#8\*\*40\*([01])\*([01])\*/gm)]
if( matches && matches.length > 0 && matches[0].length > 0 ) {
this.sipCamera.console.debug( "Answering machine state: " + matches[0][1] + " / Welcome message state: " + matches[0][2] );
this.aswmIsEnabled = matches[0][1] == '1';
if( this.isEnabled() ) {
this.sipCamera.console.debug("Handling incoming answering machine reply")
const messages : string[] = message.split(';')
Expand All @@ -60,6 +62,8 @@ export class VoicemailHandler extends SipRequestHandler {
this.sipCamera.console.debug("No new messages since: " + lastVoicemailMessageTimestamp + " lastMessage: " + lastMessageTimestamp)
}
}
} else {
this.sipCamera.console.debug("Not handling message: " + message)
}
}

Expand Down

0 comments on commit 6dba80c

Please sign in to comment.