├── .gitignore ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── HomeAssistant └── config │ └── www │ └── doordroid │ ├── doordroid-card.js │ └── jssip-3.3.6.min.js ├── Readme.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── be │ │ └── rosoco │ │ └── doordroid │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ ├── be │ │ │ └── rosoco │ │ │ │ └── doordroid │ │ │ │ ├── AbstractPreference.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── OnBoardingActivity1.kt │ │ │ │ ├── OnBoardingActivity2.kt │ │ │ │ ├── OnBoardingActivity3.kt │ │ │ │ ├── SplashActivity.kt │ │ │ │ ├── fotoapparat │ │ │ │ ├── Camera.kt │ │ │ │ └── CameraStreamListener.kt │ │ │ │ ├── httpd │ │ │ │ ├── CallHttpRequestHandler.kt │ │ │ │ ├── CameraStillImageHttpRequestHandler.kt │ │ │ │ ├── CameraStreamImageHttpRequestHandler.kt │ │ │ │ ├── HelloWorldHttpRequestHandler.kt │ │ │ │ ├── HttpRequestHandler.kt │ │ │ │ ├── Httpd.kt │ │ │ │ └── StatusHttpRequestHandler.kt │ │ │ │ └── sip │ │ │ │ └── DoorSip.kt │ │ └── fi │ │ │ └── iki │ │ │ └── elonen │ │ │ └── NanoHTTPD.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_onboarding_1.xml │ │ ├── activity_onboarding_2.xml │ │ └── activity_onboarding_3.xml │ │ ├── menu │ │ └── main_menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ ├── settings_homeassistant.xml │ │ └── settings_sip.xml │ └── test │ └── java │ └── be │ └── rosoco │ └── doordroid │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /HomeAssistant/config/www/doordroid/doordroid-card.js: -------------------------------------------------------------------------------- 1 | class DoorPiCard extends HTMLElement { 2 | constructor() { 3 | super(); 4 | this.attachShadow({ mode: 'open' }); 5 | } 6 | 7 | set hass(hass) { 8 | if(this.notYetInitialized()) { 9 | this.initJsSIPIfNecessary(hass); 10 | this.initCameraView(hass); 11 | } else if(this.cameraEntityHasNewAccessToken(hass)) { 12 | this.updateCameraView(hass); 13 | } 14 | this.initDoorbellRinging(hass) 15 | } 16 | 17 | setConfig(config) { 18 | if (!config.camera_entity) { 19 | throw new Error('You need to define a camera entity'); 20 | } 21 | if(!config.sip_settings) { 22 | throw new Error('You need to define the SIP settings'); 23 | } else { 24 | if(!config.sip_settings.sip_wss_url) throw new Error('You need to define the SIP Secure Webservice url'); 25 | if(!config.sip_settings.sip_server) throw new Error('You need to define the SIP Server (ip or hostname)'); 26 | if(!config.sip_settings.sip_username) throw new Error('You need to define the SIP username'); 27 | if(!config.sip_settings.sip_password) throw new Error('You need to define the SIP password'); 28 | } 29 | this.config = config; 30 | 31 | const root = this.shadowRoot; 32 | if (root.lastChild) root.removeChild(root.lastChild); 33 | 34 | 35 | const card = document.createElement('ha-card'); 36 | const content = document.createElement('div'); 37 | const style = document.createElement('style'); 38 | style.textContent = ` 39 | ha-card { 40 | /* sample css */ 41 | } 42 | .buttons { 43 | overflow: auto; 44 | padding: 16px; 45 | text-align: right; 46 | } 47 | .ring-buttons { 48 | float: left; 49 | background: var(--paper-dialog-background-color, var(--primary-background-color)); 50 | color: var(--paper-dialog-color, var(--primary-text-color)); 51 | } 52 | #cameraview img{ 53 | object-fit: cover; 54 | height: 400px; 55 | } 56 | mwc-button { 57 | margin-right: 16px; 58 | } 59 | `; 60 | content.innerHTML = ` 61 |
62 |

Initializing SIP connection and webcam view

63 | 64 |
65 |
66 |
67 | 68 | 69 | 70 |
71 | ` + 'Open Door' + ` 72 | 73 | 74 | 75 |
76 | `; 77 | card.header = '' 78 | card.appendChild(content); 79 | card.appendChild(style); 80 | root.appendChild(card); 81 | } 82 | 83 | // The height of your card. Home Assistant uses this to automatically 84 | // distribute all cards over the available columns. 85 | getCardSize() { 86 | return 3; 87 | } 88 | 89 | notYetInitialized() { 90 | return window.JsSIP && !this.sipPhone && this.config; 91 | } 92 | 93 | initJsSIPIfNecessary(hass) { 94 | console.log('Loading SIPPhone'); 95 | 96 | let socket = new JsSIP.WebSocketInterface(this.config.sip_settings.sip_wss_url); 97 | let configuration = { 98 | sockets : [ socket ], 99 | uri : `sip:${this.config.sip_settings.sip_username}@${this.config.sip_settings.sip_server}`, 100 | password : this.config.sip_settings.sip_password 101 | }; 102 | this.sipPhone = new JsSIP.UA(configuration); 103 | this.sipPhone.start() 104 | 105 | 106 | let callOptions = { mediaConstraints: { audio: true, video: false } };// only audio calls 107 | 108 | this.sipPhone.on("registered", () => console.log('SIPPhone registered with SIP Server')); 109 | 110 | let droidCard = this; 111 | this.sipPhone.on("newRTCSession", function(data){ 112 | let session = data.session; 113 | 114 | 115 | if (session.direction === "incoming") { 116 | let acceptCallBtn = droidCard.getElementById('btn-accept-call'); 117 | let rejectCallBtn = droidCard.getElementById('btn-reject-call'); 118 | let endCallBtn = droidCard.getElementById('btn-end-call'); 119 | 120 | session.on("accepted", () => { 121 | console.log('call accepted') 122 | acceptCallBtn.style.display = 'none'; 123 | rejectCallBtn.style.display = 'none'; 124 | endCallBtn.style.display = 'inline-flex'; 125 | 126 | }); 127 | session.on("confirmed", () => console.log('call confirmed')); 128 | session.on("ended", () => {console.log('call ended'); droidCard.cleanup(hass)}); 129 | session.on("failed", () =>{console.log('call failed'); droidCard.cleanup(hass)}); 130 | session.on("peerconnection", () => { 131 | session.connection.addEventListener("addstream", (e) => { 132 | console.log('adding audiostream') 133 | // set remote audio stream (to listen to remote audio) 134 | // remoteAudio is