-
-
Notifications
You must be signed in to change notification settings - Fork 269
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* * Fix an issues in SIP.js where the ACK and BYE replies didn't go to the correct uri * * Implemented outgoing SIP MESSAGE sending * Adding voice mail check * Adding a lock for a bticino doorbell * Cleanup dependencies, code in sip, bticino plugins * Cleanup dependencies, code in sip, bticino plugins * Clear stale devices from our map and clear the voicemail check * Do not require register() for a SIP call * Narrow down the event matching to deletes of devices * Use releaseDevice to clean up stale entries * Fix uuid version * Attempt to make two way audio work * Attempt to make two way audio work - fine tuning * Enable incoming doorbell events * SipCall was never a "sip call" but more like a manager SipSession was more the "sip call" * * Rename sip registered session to persistent sip manager * Allow handling of call pickup in homekit (hopefully!) * * use the consoles from the camera object * * use the consoles from the camera object * * Fix the retry timer * * Added webhook url * * parse record route correctly * * Add gruu and use a custom fork of sip.js which supports keepAlive SIP clients (and dropped Websocket) * use cross-env in package.json * Added webhook urls for faster handling of events * Added videoclips * plugins/sip 0.0.6 * plugins/bticino 0.0.7 * Implemented Reboot interface * v0.0.9 which works with c300-controller * better validation during creation of device * automatically sets the correct settings depending on the data sent back from the controller --------- Co-authored-by: Marc Vanbrabant <[email protected]>
- Loading branch information
Showing
10 changed files
with
254 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,9 @@ | |
|
||
The C300X Plugin for Scrypted allows viewing your C300X intercom with incoming video/audio. | ||
|
||
WARNING: You will need access to the device, see https://github.com/fquinto/bticinoClasse300x | ||
WARNING: You will need access to the device, see https://github.com/fquinto/bticinoClasse300x. | ||
|
||
You also need the **[c300x-controller](https://github.com/slyoldfox/c300x-controller)** and node (v17.9.1) running on your device which will expose an API for the intercom. | ||
|
||
## Development instructions | ||
|
||
|
@@ -17,12 +19,37 @@ $ num run scrypted-deploy 127.0.0.1 | |
|
||
After flashing a custom firmware you must at least: | ||
|
||
* Install [node](https://nodejs.org/download/release/latest-v17.x/node-v17.9.1-linux-armv7l.tar.gz) on your device and run the c300x-controller on the device | ||
* Install [/lib/libatomic.so.1](http://ftp.de.debian.org/debian/pool/main/g/gcc-10-cross/libatomic1-armhf-cross_10.2.1-6cross1_all.deb) in **/lib** | ||
* Allow access to the SIP server on port 5060 | ||
* Allow your IP to authenticated with the SIP server | ||
* Add a SIP user for scrypted | ||
|
||
To do this use the guide below: | ||
|
||
## Installing node and c300x-controller | ||
|
||
``` | ||
$ cd /home/bticino/cfg/extra/ | ||
$ mkdir node | ||
$ cd node | ||
$ wget https://nodejs.org/download/release/latest-v17.x/node-v17.9.1-linux-armv7l.tar.gz | ||
$ tar xvfz node-v17.9.1-linux-armv7l.tar.gz | ||
``` | ||
|
||
Node will require libatomic.so.1 which isn't shipped with the device, get the .deb file from http://ftp.de.debian.org/debian/pool/main/g/gcc-10-cross/libatomic1-armhf-cross_10.2.1-6cross1_all.deb | ||
|
||
``` | ||
$ ar x libatomic1-armhf-cross_10.2.1-6cross1_all.deb | ||
``` | ||
|
||
scp the `libatomic.so.1` to `/lib` and check that node works: | ||
|
||
``` | ||
$ root@C3X-00-00-00-00-00--2222222:~# /home/bticino/cfg/extra/node/bin/node -v | ||
v17.9.1 | ||
``` | ||
|
||
## Make flexisip listen on a reachable IP and add users to it | ||
|
||
To be able to talk to our own SIP server, we need to make the SIP server on the C300X | ||
|
@@ -93,15 +120,15 @@ hashed-passwords=true | |
reject-wrong-client-certificates=true | ||
```` | ||
|
||
Now we will add a `user agent` (user) that will be used by `baresip` to register itself with `flexisip` | ||
Now we will add a `user agent` (user) that will be used by `scrypted` to register itself with `flexisip` | ||
|
||
Edit the `/etc/flexisip/users/users.db.txt` file and create a new line by copy/pasting the c300x user. | ||
|
||
For example: | ||
|
||
```` | ||
[email protected] md5:ffffffffffffffffffffffffffffffff ; | ||
baresip@1234567.bs.iotleg.com md5:ffffffffffffffffffffffffffffffff ; | ||
scrypted@1234567.bs.iotleg.com md5:ffffffffffffffffffffffffffffffff ; | ||
```` | ||
|
||
Leave the md5 as the same value - I use `fffff....` just for this example. | ||
|
@@ -110,7 +137,7 @@ Edit the `/etc/flexisip/users/route.conf` file and add a new line to it, it spec | |
Change the IP address to the place where you will run `baresip` (same as `trusted-hosts` above) | ||
|
||
```` | ||
<sip:baresip@1234567.bs.iotleg.com> <sip:192.168.0.XX> | ||
<sip:scrypted@1234567.bs.iotleg.com> <sip:192.168.0.XX> | ||
```` | ||
|
||
Edit the `/etc/flexisip/users/route_int.conf` file. | ||
|
@@ -121,7 +148,7 @@ You can look at it as a group of users that is called when you call `alluser@123 | |
|
||
Add your username at the end (make sure you stay on the same line, NOT a new line!) | ||
```` | ||
<sip:[email protected]> ..., <sip:baresip@1234567.bs.iotleg.com> | ||
<sip:[email protected]> ..., <sip:scrypted@1234567.bs.iotleg.com> | ||
```` | ||
|
||
Reboot and verify flexisip is listening on the new IP address. | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import * as nodeIp from "ip"; | ||
import { get } from 'http' | ||
import * as net from 'net' | ||
import { BticinoSipCamera } from "./bticino-camera"; | ||
import { SipHelper } from './sip-helper'; | ||
|
||
export class ControllerApi { | ||
private timeout : NodeJS.Timeout | ||
|
||
constructor( private sipCamera : BticinoSipCamera ) { | ||
this.timeout = setTimeout( () => { | ||
// Delay a bit an run in a different thread in case this fails | ||
this.registerEndpoints( true ) | ||
}, 5000 ) | ||
} | ||
|
||
/** | ||
* Will validate certain requirements for scrypted to work correctly with the intercom: | ||
*/ | ||
public static validate( ipAddress ) { | ||
return this.validateFlexisipSipPort(ipAddress).then( this.validateController ) | ||
} | ||
|
||
/** | ||
* Will validate if the non secure SIP port was opened after modifying /etc/init.d/flexisipsh | ||
*/ | ||
private static validateFlexisipSipPort( ipAddress : string ) : Promise<string> { | ||
let conn = net.createConnection( { host: ipAddress, port: 5060, timeout: 5000 } ) | ||
return new Promise( (resolve, reject) => { | ||
conn.setTimeout(5000); | ||
conn.on('connect', () => resolve( ipAddress )); | ||
conn.on('timeout', () => reject( new Error("Timeout connecting to port 5060, is this a Bticino intercom? Did you change /etc/init.d/flexisipsh to make it listen on this port?") ) ); | ||
conn.on('error', () => reject( new Error("Error connecting to port 5060, is this a Bticino intercom? Did you change /etc/init.d/flexisipsh to make it listen on this port?") ) ); | ||
}) | ||
} | ||
|
||
/** | ||
* Will validate if the c300x-controller is running on port 8080. | ||
* The c300x-controller will return errors if some configuration errors are present on the intercom. | ||
*/ | ||
private static validateController( ipAddress : string ) : Promise<void> { | ||
// Will throw an exception if invalid format | ||
const c300x = nodeIp.toBuffer( ipAddress ) | ||
const validatedIp = nodeIp.toString(c300x) | ||
|
||
const url = `http://${validatedIp}:8080/validate-setup?raw=true` | ||
|
||
return new Promise( (resolve, reject) => get(url, (res) => { | ||
let body = ""; | ||
res.on("data", data => { body += data }); | ||
res.on("end", () => { | ||
try { | ||
let parsedBody = JSON.parse( body ) | ||
if( parsedBody["errors"].length > 0 ) { | ||
reject( new Error( parsedBody["errors"][0] ) ) | ||
} else { | ||
parsedBody["ipAddress"] = validatedIp | ||
resolve( parsedBody ) | ||
} | ||
} catch( e ) { | ||
reject( e ) | ||
} | ||
}) | ||
res.on("error", (e) => { reject(e)}) | ||
if( res.statusCode != 200 ) { | ||
reject( new Error(`Could not validate required c300x-controller. Check ${url}`) ) | ||
} | ||
} ).on("error", (e) => { reject(`Could not connect to the c300x-controller at ${url}`) }) ) | ||
} | ||
|
||
/** | ||
* This verifies if the intercom is customized correctly. It verifies: | ||
* | ||
* - if a dedicated scrypted sip user is added for this specific camera instance in /etc/flexisip/users/users.db.txt | ||
* - if this dedicated scrypted sip user is configured in /etc/flexisip/users/route.conf and /etc/flexisip/users/route_int.conf | ||
*/ | ||
public registerEndpoints( verifyUser : boolean ) { | ||
let ipAddress = SipHelper.getIntercomIp(this.sipCamera) | ||
let sipFrom = SipHelper.getIdentifier(this.sipCamera) | ||
const pressed = Buffer.from(this.sipCamera.doorbellWebhookUrl + 'pressed').toString('base64') | ||
const locked = Buffer.from(this.sipCamera.doorbellLockWebhookUrl + 'locked').toString('base64') | ||
const unlocked = Buffer.from(this.sipCamera.doorbellLockWebhookUrl + 'unlocked').toString('base64') | ||
get(`http://${ipAddress}:8080/register-endpoint?raw=true&identifier=${sipFrom}&pressed=${pressed}&locked=${locked}&unlocked=${unlocked}&verifyUser=${verifyUser}`, (res) => { | ||
if( verifyUser ) { | ||
let body = ""; | ||
res.on("data", data => { body += data }); | ||
res.on("end", () => { | ||
try { | ||
let parsedBody = JSON.parse( body ) | ||
if( parsedBody["errors"].length > 0 ) { | ||
this.sipCamera.log.a("This camera is not setup correctly, it will not be able to receive the incoming doorbell stream. Check the console for the errors.") | ||
parsedBody["errors"].forEach( error => { | ||
this.sipCamera.console.error( "ERROR: " + error ) | ||
}); | ||
} | ||
} catch( e ) { | ||
this.sipCamera.console.error("Error parsing body to JSON: " + body ) | ||
} | ||
}) | ||
} | ||
console.log("Endpoint registration status: " + res.statusCode) | ||
}); | ||
|
||
// The default evict time on the c300x-controller is 5 minutes, so this will certainly be within bounds | ||
this.timeout = setTimeout( () => this.registerEndpoints( false ) , 2 * 60 * 1000 ) | ||
} | ||
|
||
/** | ||
* Informs the c300x-controller where to send the stream to | ||
*/ | ||
public updateStreamEndpoint() : Promise<void> { | ||
let ipAddress = SipHelper.getIntercomIp(this.sipCamera) | ||
let sipFrom = SipHelper.getIdentifier(this.sipCamera) | ||
return new Promise( (resolve, reject) => get(`http://${ipAddress}:8080/register-endpoint?raw=true&updateStreamEndpoint=${sipFrom}`, (res) => { | ||
if( res.statusCode != 200 ) reject( "ERROR: Could not update streaming endpoint, call returned: " + res.statusCode ) | ||
else resolve() | ||
} ) ); | ||
} | ||
|
||
public cancelTimer() { | ||
if( this.timeout ) { | ||
clearTimeout(this.timeout) | ||
} | ||
} | ||
} |
Oops, something went wrong.