├── .npmignore ├── lib ├── ifttt.js ├── pushcut.js ├── email.js └── pushover.js ├── configuration-examples ├── ifttt.example.json ├── pushcut.example.json ├── email.unsecure.example.json ├── email.secure.example.json ├── pushover.example.json └── advanced.example.json ├── LICENSE ├── package.json ├── CHANGELOG.md ├── index.js ├── README.md └── config.schema.json /.npmignore: -------------------------------------------------------------------------------- 1 | configuration-examples -------------------------------------------------------------------------------- /lib/ifttt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const IFTTT = require('ifttt-webhooks-channel') 4 | 5 | module.exports = class IftttMessenger { 6 | constructor(key, event, value1, value2, value3) { 7 | this.iftttKey = key 8 | this.iftttEvent = event 9 | this.value_1 = value1 10 | this.value_2 = value2 11 | this.value_3 = value3 12 | } 13 | 14 | 15 | getRecipient() { 16 | return this.iftttKey + " (event : " + this.iftttEvent + ")" 17 | } 18 | 19 | 20 | sendMessage() { 21 | const ifttt = new IFTTT(this.iftttKey) 22 | ifttt.post(this.iftttEvent, [this.value_1, this.value_2, this.value_3]) 23 | .catch(error => new Error(error)) 24 | } 25 | } -------------------------------------------------------------------------------- /configuration-examples/ifttt.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "bridge": { 3 | "name": "Homebridge-Test", 4 | "username": "0E:2E:91:F3:98:7A", 5 | "port": 51262, 6 | "pin": "031-45-154" 7 | }, 8 | "accessories": [ 9 | { 10 | "accessory": "HomebridgeMessenger", 11 | "name": "Messenger", 12 | "services": { 13 | "ifttt": { 14 | "key": "your_key" 15 | } 16 | }, 17 | "messages": [ 18 | { 19 | "type": "ifttt", 20 | "name": "Test IFTTT", 21 | "event": "my_webhook", 22 | "value1": "hello world", 23 | "value2": "foobar", 24 | "value3": "chewbacca" 25 | } 26 | ] 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /configuration-examples/pushcut.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "bridge": { 3 | "name": "Homebridge-Test", 4 | "username": "0E:2E:91:F3:98:7A", 5 | "port": 51262, 6 | "pin": "031-45-154" 7 | }, 8 | "accessories": [ 9 | { 10 | "accessory": "HomebridgeMessenger", 11 | "name": "Messenger", 12 | "services": { 13 | "pushcut": { 14 | "apikey": "your_key" 15 | } 16 | }, 17 | "messages": [ 18 | { 19 | "type": "pushcut", 20 | "name": "Test pushcut", 21 | "notification": "Homebridge-Test", 22 | "title": "My notification ", 23 | "text": "Hello world" 24 | } 25 | ] 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /configuration-examples/email.unsecure.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "bridge": { 3 | "name": "Homebridge-Test", 4 | "username": "0E:2E:91:F3:98:7A", 5 | "port": 51262, 6 | "pin": "031-45-154" 7 | }, 8 | "accessories": [ 9 | { 10 | "accessory": "HomebridgeMessenger", 11 | "name": "Messenger", 12 | "services": { 13 | "email": { 14 | "recipient": "you_email@domain.com", 15 | "smtpServer": "smtp.domain.com", 16 | "smtpPort": 25, 17 | "smtpSecure": false 18 | } 19 | }, 20 | "messages": [ 21 | { 22 | "type": "email", 23 | "name": "Test unsecured email", 24 | "text": "This is a test", 25 | "recipients": "other_email@domain.com" 26 | } 27 | ] 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /configuration-examples/email.secure.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "bridge": { 3 | "name": "Homebridge-Test", 4 | "username": "0E:2E:91:F3:98:7A", 5 | "port": 51262, 6 | "pin": "031-45-154" 7 | }, 8 | "accessories": [ 9 | { 10 | "accessory": "HomebridgeMessenger", 11 | "name": "Messenger", 12 | "services": { 13 | "email": { 14 | "recipient": "you_email@domain.com", 15 | "smtpServer": "smtp.domain.com", 16 | "smtpPort": 465, 17 | "smtpSecure": true, 18 | "smtpUsername": "your_username", 19 | "smtpPassword": "your_password" 20 | } 21 | }, 22 | "messages": [ 23 | { 24 | "type": "email", 25 | "name": "Test email", 26 | "text": "This is a test" 27 | } 28 | ] 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Paul-Olivier Trudeau 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/pushcut.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const axios = require('axios') 4 | 5 | module.exports = class IftttMessenger { 6 | constructor(apiKey, notificationName, messageTitle, messageText, messageInput, messageActions) { 7 | this.pushcut_apikey = apiKey 8 | this.message_notification = notificationName 9 | this.message_title = messageTitle 10 | this.message_text = messageText 11 | this.message_input = messageInput 12 | this.message_actions = messageActions 13 | } 14 | 15 | 16 | getRecipient() { 17 | return this.pushcut_apikey + " (notification : " + this.message_notification + ")" 18 | } 19 | 20 | 21 | sendMessage() { 22 | var url = "https://api.pushcut.io/v1/notifications/" + this.message_notification 23 | var data = { text: this.message_text, title: this.message_title, input : this.message_input, actions: this.message_actions}; 24 | var config = { headers: {'accept': '*/*', 'API-Key': this.pushcut_apikey, 'Content-Type': 'application/json'}}; 25 | 26 | axios.post(url, data, config) 27 | .catch(error => { 28 | console.error(error) 29 | }) 30 | 31 | } 32 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "homebridge-messenger", 3 | "version": "0.0.8", 4 | "description": "Send HomeKit messages with HomeBridge (Pushover / IFTTT / Email)", 5 | "author": "Paul-Olivier Trudeau (p-o.ca)", 6 | "license": "MIT", 7 | "main": "index.js", 8 | "private": false, 9 | "homepage": "https://github.com/potrudeau/homebridge-messenger", 10 | "bugs": { 11 | "url": "https://github.com/potrudeau/homebridge-messenger/issues" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/potrudeau/homebridge-messenger.git" 16 | }, 17 | "scripts": { 18 | "test": "echo \"Error: no test specified\" && exit 1" 19 | }, 20 | "keywords": [ 21 | "homebridge-plugin", 22 | "homebridge", 23 | "pushover", 24 | "email", 25 | "mail", 26 | "ifttt", 27 | "maker", 28 | "webhooks" 29 | ], 30 | "engines": { 31 | "node": ">=6.0.0", 32 | "homebridge": ">=0.2.5" 33 | }, 34 | "dependencies": { 35 | "nodemailer": "^6.4.17", 36 | "pushover-notifications": "^1.2.2", 37 | "node-persist": "^2.1.0", 38 | "ifttt-webhooks-channel": "^1.1.5", 39 | "axios": "^0.21.1" 40 | } 41 | } -------------------------------------------------------------------------------- /configuration-examples/pushover.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "bridge": { 3 | "name": "Homebridge-Test", 4 | "username": "0E:2E:91:F3:98:7A", 5 | "port": 51262, 6 | "pin": "031-45-154" 7 | }, 8 | "accessories": [ 9 | { 10 | "accessory": "HomebridgeMessenger", 11 | "name": "Messenger", 12 | "services": { 13 | "pushover": { 14 | "user": "your_user", 15 | "token": "your_token" 16 | } 17 | }, 18 | "messages": [ 19 | { 20 | "type": "pushover", 21 | "name": "Pushover message", 22 | "text": "This is a test", 23 | "sound": "pushover", 24 | "device": "iphone", 25 | "priority": 0 26 | }, 27 | { 28 | "type": "pushover", 29 | "name": "Critical message", 30 | "text": "This is important", 31 | "sound": "magic", 32 | "priority": 2 33 | } 34 | ] 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /configuration-examples/advanced.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "bridge": { 3 | "name": "Homebridge-Test", 4 | "username": "0E:2E:91:F3:98:7A", 5 | "port": 51262, 6 | "pin": "031-45-154" 7 | }, 8 | "accessories": [ 9 | { 10 | "accessory": "HomebridgeMessenger", 11 | "name": "Messenger", 12 | "services": { 13 | "pushover": { 14 | "user": "[YOUR USER]", 15 | "token": "[YOUR TOKEN]" 16 | }, 17 | "email": { 18 | "recipient": "you_email@domain.com", 19 | "smtpServer": "smtp.domain.com", 20 | "smtpPort": 465, 21 | "smtpSecure": true, 22 | "smtpUsername": "your_username", 23 | "smtpPassword": "your_password" 24 | }, 25 | "ifttt": { 26 | "key": "your_key" 27 | } 28 | }, 29 | "messages": [ 30 | { 31 | "type": "pushover", 32 | "name": "Pushover message", 33 | "text": "This is a test", 34 | "sound": "pushover", 35 | "priority": 0 36 | }, 37 | { 38 | "type": "pushover", 39 | "name": "Critical message", 40 | "text": "This is important", 41 | "sound": "magic", 42 | "device": "iphone", 43 | "priority": 2 44 | }, 45 | { 46 | "type": "email", 47 | "name": "Test email", 48 | "text": "This is a test" 49 | }, 50 | { 51 | "type": "ifttt", 52 | "name": "Test IFTTT", 53 | "event": "my_webhook", 54 | "value1": "hello world", 55 | "value2": "foobar", 56 | "value3": "chewbacca" 57 | } 58 | ] 59 | } 60 | ] 61 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. This project uses [Semantic Versioning](https://semver.org/). 4 | 5 | ## 0.0.8 (2021-01-12) 6 | 7 | ### Bug fixes 8 | * Fix problem when a message is send when the master switch is off ([#18](https://github.com/potrudeau/homebridge-messenger/issues/18)) 9 | * Updated some plugin dependencies 10 | 11 | ## 0.0.7 (2021-01-04) 12 | 13 | ### Bug fixes 14 | * Fix problem with secure emails crashing plugin. ([#15](https://github.com/potrudeau/homebridge-messenger/issues/15)) 15 | 16 | ### Features 17 | 18 | * **Email messages :** Added support for alternate email override. ([#14](https://github.com/potrudeau/homebridge-messenger/issues/14)) 19 | * **Email messages :** Added support for multiple email recipients (in alternate email). ([#13](https://github.com/potrudeau/homebridge-messenger/issues/13)) 20 | 21 | ## 0.0.6 (2020-04-26) 22 | 23 | ### Bug fixes 24 | * Bug fixe when Pushover device is not provided. ([#6](https://github.com/potrudeau/homebridge-messenger/issues/6)) 25 | 26 | ## 0.0.5 (2020-04-24) 27 | 28 | ### Features 29 | 30 | * **IFTTT messages :** Added support for [IFTTT Webhooks](https://ifttt.com/maker_webhooks). ([#1](https://github.com/potrudeau/homebridge-messenger/issues/1)) 31 | * **Pushover messages :** Added Url and Url Title properties for Pushover messages. Also, Pushover messages now supports HTML code, as permitted by the Pushover [API](https://pushover.net/api). ([#3](https://github.com/potrudeau/homebridge-messenger/issues/3)) 32 | 33 | ### Bug fixes 34 | * Remove all spaces from "device" property of Pushover messages. The spaces were not allowing message to be sent to multiple devices. ([#4](https://github.com/potrudeau/homebridge-messenger/issues/4)) 35 | 36 | ## 0.0.4 (2020-04-21) 37 | 38 | ### Features 39 | 40 | * **Pushover messages :** Added [Device](https://pushover.net/api#identifiers) property. 41 | 42 | ## 0.0.3 (2020-04-19) 43 | 44 | ### Features 45 | 46 | * **Caching for main switch :** The main switch now keeps its status (ON/OFF) after a Homebridge restart. 47 | 48 | ## 0.0.2 (2020-04-13) 49 | 50 | ### Features 51 | 52 | * **Support for Homebridge Config UI X :** Homebridge-messenger is now compatible with [Plugin Settings GUI](https://github.com/oznu/homebridge-config-ui-x/wiki/Developers:-Plugin-Settings-GUI). 53 | * **Email messages :** Added default value for SMTP port (25). 54 | * **Logging :** Added more logging when loading plugin. 55 | 56 | ## 0.0.1 (2020-04-09) 57 | 58 | ### Features 59 | 60 | * **Initial release:** : Basic support for Pushover messages and emails! 61 | -------------------------------------------------------------------------------- /lib/email.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Nodemailer = require('nodemailer'); 4 | 5 | module.exports = class EmailMessenger { 6 | constructor(emailRecipient, smtpServer, smtpPort, smtpSecure, smtpUsername, smtpPassword, messageTitle, messageText, messageRecipients) { 7 | this.default_email = emailRecipient 8 | this.smtp_server = smtpServer 9 | this.smtp_port = smtpPort 10 | this.smtp_secure = smtpSecure 11 | this.smtp_username = smtpUsername 12 | this.smtp_password = smtpPassword 13 | this.message_title = messageTitle 14 | this.message_text = messageText 15 | this.message_recipients = messageRecipients 16 | 17 | if (!this.default_email) 18 | throw new Error(this.message_title + " : Email cannot be empty"); 19 | 20 | if (!this.smtp_server) 21 | throw new Error(this.message_title + " : SMTP server cannot be empty"); 22 | 23 | if (!this.smtp_port) 24 | this.smtp_port = 25; 25 | 26 | if (this.smtp_port < 0 || this.smtp_port > 65535) 27 | throw new Error(this.message_title + " : SMTP port must be between 0 and 65535"); 28 | 29 | if (!this.smtp_secure) 30 | this.smtp_secure = false; 31 | 32 | if (!this.message_title) 33 | throw new Error(this.message_title + " : Message title cannot be empty"); 34 | 35 | if (!this.message_text) 36 | throw new Error(this.message_title + " : Message text cannot be empty"); 37 | } 38 | 39 | 40 | getRecipient() { 41 | if (this.message_recipients) 42 | return this.message_recipients; 43 | else 44 | return this.default_email; 45 | } 46 | 47 | 48 | sendMessage() { 49 | 50 | if (this.smtp_username) { 51 | var transport = Nodemailer.createTransport({ 52 | host: this.smtp_server, port: this.smtp_port, 53 | auth: { 54 | user: this.smtp_username, pass: this.smtp_password 55 | }, 56 | tls: { 57 | requireTLS: this.smtp_secure 58 | } 59 | }); 60 | } 61 | else { 62 | var transport = Nodemailer.createTransport({ 63 | host: this.smtp_server, port: this.smtp_port 64 | }); 65 | } 66 | 67 | var mailOptions = { 68 | from: this.default_email, to: this.getRecipient(), subject: this.message_title, text: this.message_text 69 | } 70 | 71 | transport.sendMail(mailOptions, (error, info) => { 72 | if (error) { 73 | throw new Error(error); 74 | } 75 | }); 76 | } 77 | } -------------------------------------------------------------------------------- /lib/pushover.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Pushover = require('pushover-notifications'); 4 | 5 | const MESSAGE_RETRY = 60; 6 | const MESSAGE_EXPIRE = 3600; 7 | const HTML = 1; 8 | 9 | module.exports = class PushOverMessenger { 10 | constructor(pushoverUser, pushoverToken, messageTitle, messageText, messagePriority, messageDevice ,messageSound, url, urlTitle) { 11 | this.pushover_user = pushoverUser 12 | this.pushover_token = pushoverToken 13 | this.message_title = messageTitle 14 | this.message_text = messageText 15 | this.message_priority = messagePriority 16 | this.message_device = messageDevice 17 | this.message_sound = messageSound 18 | this.message_url = url 19 | this.message_urltitle = urlTitle 20 | 21 | if (!this.pushover_user) 22 | throw new Error(this.message_title + " : User cannot be empty"); 23 | 24 | if (!this.pushover_token) 25 | throw new Error(this.message_title + " : Token cannot be empty"); 26 | 27 | if (!this.message_title) 28 | throw new Error(this.message_title + " : Message title cannot be empty"); 29 | 30 | if (!this.message_text) 31 | throw new Error(this.message_title + " : Message text cannot be empty"); 32 | 33 | if (![-2, -1, 0 , 1, 2].includes(this.message_priority)) 34 | throw new Error(this.message_title + " : Invalid priority value " + this.message_priority); 35 | 36 | if (this.message_device) 37 | this.message_device = this.message_device.replace(/\s/g,'') 38 | 39 | if (!["pushover", "bike", "bugle", "cashregister", "classical", "cosmic", "falling", "gamelan", 40 | "incoming", "intermission", "magic", "mechanical", "pianobar", "siren", "spacealarm", "tugboat", 41 | "alien", "climb", "persistent", "echo", "updown", "none"].includes(this.message_sound)) 42 | this.message_sound = "pushover"; 43 | } 44 | 45 | 46 | getRecipient() { 47 | return this.pushover_token 48 | } 49 | 50 | 51 | sendMessage() { 52 | var p = new Pushover( { 53 | user: this.pushover_user, 54 | token: this.pushover_token, 55 | }) 56 | 57 | var msg = { 58 | message: this.message_text, 59 | title: this.message_title, 60 | device : this.message_device, 61 | priority: this.message_priority, 62 | sound : this.message_sound, 63 | url : this.message_url, 64 | url_title : this.message_urltitle, 65 | html: HTML, 66 | retry : MESSAGE_RETRY, 67 | expire: MESSAGE_EXPIRE 68 | } 69 | 70 | p.send(msg, function(error, result) { 71 | if (error) 72 | throw new Error(error); 73 | }) 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var PushOverMessenger = require('./lib/pushover.js') 4 | var EmailMessenger = require('./lib/email.js') 5 | var IftttMessenger = require('./lib/ifttt.js') 6 | var PushcutMessenger = require('./lib/pushcut.js') 7 | 8 | let Service, Characteristic, HomebridgeAPI 9 | 10 | module.exports = (homebridge) => { 11 | Service = homebridge.hap.Service 12 | Characteristic = homebridge.hap.Characteristic 13 | HomebridgeAPI = homebridge 14 | homebridge.registerAccessory('homebridge-messenger', 'HomebridgeMessenger', HomebridgeMessenger) 15 | } 16 | 17 | 18 | class HomebridgeMessenger { 19 | constructor (log, config) { 20 | this.log = log 21 | this.config = config 22 | 23 | // Add main switch to Homebride 24 | this.serviceMainSwitch = new Service.Switch(this.config.name, 0) 25 | this.log("Added Main Switch : " + this.config.name); 26 | 27 | // Initialize cache 28 | this.cacheDirectory = HomebridgeAPI.user.persistPath(); 29 | this.storage = require('node-persist'); 30 | this.storage.initSync({dir:this.cacheDirectory, forgiveParseErrors: true}); 31 | 32 | // Get cache and validate if main switch is in cache 33 | var cachedState = this.storage.getItemSync(this.config.name); 34 | if((cachedState === undefined) || (cachedState === false)) { // If not in cache 35 | this.isOn = false 36 | this.serviceMainSwitch.setCharacteristic(Characteristic.On, false); 37 | this.log("Main Switch status"+ " : " + false); 38 | } else { // If in cache 39 | this.isOn = true 40 | this.serviceMainSwitch.setCharacteristic(Characteristic.On, true); 41 | this.log("Main Switch status"+ " : " + true); 42 | } 43 | 44 | // Load configured messages 45 | this.loadMessages(); 46 | } 47 | 48 | loadMessages() 49 | { 50 | this.messages = this.config.messages || []; 51 | this.serviceMessagesSwitches = []; 52 | 53 | // Iterate through configured messages 54 | for (let x = 0; x < this.messages.length; x++) { 55 | 56 | // Add switch for each message 57 | let serviceMessageSwitch = new Service.Switch(this.messages[x].name , x + 100) 58 | this.log("Added " + this.messages[x].type.toLowerCase() + " : " + this.messages[x].name); 59 | 60 | // Add event handler for each message 61 | serviceMessageSwitch.getCharacteristic(Characteristic.On) .on('set', function(value, callback) { 62 | if (value==true) { // If message switch status is On 63 | if (this.isOn) { // If main switch status if On 64 | var message; 65 | switch(this.messages[x].type.toLowerCase()) { 66 | 67 | // Message type is email 68 | case "email": 69 | message = new EmailMessenger(this.config.services.email.recipient, 70 | this.config.services.email.smtpServer, 71 | this.config.services.email.smtpPort, 72 | this.config.services.email.smtpSecure, 73 | this.config.services.email.smtpUsername, 74 | this.config.services.email.smtpPassword, 75 | this.messages[x].name, 76 | this.messages[x].text, 77 | this.messages[x].recipients) 78 | break; 79 | 80 | // Message type is pushover 81 | case "pushover": 82 | message = new PushOverMessenger(this.config.services.pushover.user, 83 | this.config.services.pushover.token, 84 | this.messages[x].name, 85 | this.messages[x].text, 86 | this.messages[x].priority, 87 | this.messages[x].device, 88 | this.messages[x].sound, 89 | this.messages[x].url, 90 | this.messages[x].urltitle) 91 | break; 92 | 93 | // Message type is ifttt 94 | case "ifttt": 95 | message = new IftttMessenger(this.config.services.ifttt.key, 96 | this.messages[x].event, 97 | this.messages[x].value1, 98 | this.messages[x].value2, 99 | this.messages[x].value3) 100 | break; 101 | 102 | // Message type is pushcut 103 | case "pushcut": 104 | message = new PushcutMessenger(this.config.services.pushcut.apikey, 105 | this.messages[x].notification, 106 | this.messages[x].title, 107 | this.messages[x].text, 108 | this.messages[x].input, 109 | this.messages[x].actions) 110 | break; 111 | 112 | // Invalid message type 113 | default: 114 | throw new Error(this.messages[x].name + " : Invalid type value."); 115 | break; 116 | } 117 | 118 | this.log(this.messages[x].name + " : Message sent to " + message.getRecipient()) 119 | message.sendMessage() 120 | } else { // If main switch status if Off 121 | this.log(this.messages[x].name + " : Message not sent. Master switch is off.") 122 | } 123 | 124 | // Configure message switch to be stateless : will be turned off after 100 ms. 125 | setTimeout(function() { 126 | serviceMessageSwitch.setCharacteristic(Characteristic.On, false); 127 | }.bind(this), 100, this.time); 128 | } 129 | 130 | callback(null); 131 | }.bind(this)); 132 | 133 | // Add message switch to array. Array will be loaded in getServices() 134 | this.serviceMessagesSwitches.push(serviceMessageSwitch); 135 | } 136 | 137 | } 138 | 139 | 140 | setOnCharacteristicHandler (value, callback) { 141 | this.isOn = value 142 | this.storage.setItemSync(this.config.name, value); 143 | this.log("Main Switch status"+ " : " + value); 144 | callback(null); 145 | } 146 | 147 | 148 | getServices () { 149 | // Load configuration information for devices 150 | const informationService = new Service.AccessoryInformation() 151 | .setCharacteristic(Characteristic.Manufacturer, require('./package.json').name) 152 | .setCharacteristic(Characteristic.SerialNumber, require('./package.json').name) 153 | .setCharacteristic(Characteristic.Model, require('./package.json').name) 154 | .setCharacteristic(Characteristic.FirmwareRevision, require('./package.json').version) 155 | 156 | // Event handler for main switch 157 | this.serviceMainSwitch.getCharacteristic(Characteristic.On).on('set', this.setOnCharacteristicHandler.bind(this)) 158 | 159 | // Send all switches to Homebridge to be added 160 | return [informationService, this.serviceMainSwitch, ...this.serviceMessagesSwitches]; 161 | } 162 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | 4 | 5 | 6 |

7 | 8 | 9 | # homebridge-messenger 10 | [![NPM downloads](https://flat.badgen.net/npm/dt/homebridge-messenger?color=blue)](https://npmjs.com/package/homebridge-messenger) 11 | [![NPM version](https://flat.badgen.net/npm/v/homebridge-messenger?color=blue)](https://npmjs.com/package/homebridge-messenger) 12 | [![GitHub issues](https://flat.badgen.net/github/open-issues/potrudeau/homebridge-messenger?label=issues&color=green)](https://github.com/potrudeau/homebridge-messenger/issues) 13 | [![GitHub pull requests](https://flat.badgen.net/github/prs/potrudeau/homebridge-messenger?label=pull%20requests&color=green)](https://github.com/potrudeau/homebridge-messenger/pulls) 14 | [![Licence](https://flat.badgen.net/npm/license/homebridge-messenger?color=red)](LICENSE) 15 | [![verified-by-homebridge](https://flat.badgen.net/badge/homebridge/verified/purple)](https://github.com/homebridge/homebridge/wiki/Verified-Plugins) 16 | 17 | [Homebridge](http://homebridge.io) plugin which allow users to send messages from [HomeKit](https://developer.apple.com/homekit/). 18 | 19 | 20 | ## Features 21 | The plugin supports the following technologies: 22 | * [Pushover](https://pushover.net/) 23 | * [IFTTT](https://ifttt.com) ([Webhooks](https://ifttt.com/maker_webhooks) service) 24 | * [SMTP email](https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol) 25 | 26 | 27 | ## Installation 28 | 29 | **Option 1: Install via Homebridge Config UI X:** 30 | 31 | Search for "messenger" in [homebridge-config-ui-x](https://github.com/oznu/homebridge-config-ui-x) and install `homebridge-messenger`. 32 | 33 | **Option 2: Manually Install:** 34 | 35 | ``` 36 | sudo npm install -g homebridge-messenger 37 | ``` 38 | 39 | ## Configuration 40 | The plugin adds to HomeKit a main switch and switches for each configured message. 41 | 42 | The accessory **name** is used to specify the name of the main switch. If this main switch if turned OFF, no message will be sent by the plugin. 43 | 44 | Message switches are stateless: they are turned OFF after being turned ON, once the message is sent. 45 | 46 | There are two levels of configuration: 47 | * **Service** level properties defines the parameters for a specific technology. All messages related to that technology will be using the same properties for all messages. 48 | * Example: All emails will use the same SMTP configuration. 49 | * **Message** level properties contains the configuration of each message. 50 | * Example: Each message can have a different title and text. 51 | 52 | All switches can be used in scenes and automation. 53 | 54 | ### Homebridge Config UI X 55 | [Homebridge Config UI X](https://github.com/oznu/homebridge-config-ui-x) is the easiest way to configure this plugin : 56 | * Every option is available through the UI 57 | * Configuration validation 58 | 59 | 60 | ### Pushover 61 | ``` 62 | "accessories": [ 63 | { 64 | "accessory": "HomebridgeMessenger", 65 | "name": "Messenger", 66 | "services": { 67 | "pushover": { 68 | "user": "your_user", 69 | "token": "your_token" 70 | } 71 | }, 72 | "messages": [ 73 | { 74 | "type": "pushover", 75 | "name": "Pushover message", 76 | "text": "This is a test", 77 | "sound": "pushover", 78 | "device" : "iphone", 79 | "priority": 0 80 | }, 81 | { 82 | "type": "pushover", 83 | "name": "Critical message", 84 | "text": "This is important", 85 | "sound": "magic", 86 | "priority": 2 87 | } 88 | ] 89 | } 90 | ] 91 | ``` 92 | * Service properties : 93 | * user *(required)*: The user/group key of your user (or you). 94 | * token *(required)*: Your application's API token. 95 | * Message properties : 96 | * type *(required)*: Must be `pushover`. 97 | * name *(required)*: Name of the switch and title of your message. 98 | * text *(required)*: Body of the message. 99 | * sound *(facultative)*: Name of the sound that will notify the user. If no valid value is provided, the default `pushover` sound will be used. For no sound, use `silent`. The [Pushover API](https://pushover.net/api#sounds) contains the list of all available sounds. 100 | * device *(facultative)*: The device name to send the message to. If not specified, the message will be send to all your devices. You can send to multiple devices by using a coma. 101 | * priority *(required)*: Priority of the message. Accepted values are `-2`, `-1`, `0`, `1` or `2`. You may refer to the [Pushover API](https://pushover.net/api#priority) for more details. Critical messages (`2`), are sent with the following parameters : 102 | * Retry : 60 seconds 103 | * Expires: 3600 seconds 104 | 105 | ### IFTTT (Webhooks service) 106 | ``` 107 | "accessories": [ 108 | { 109 | "accessory": "HomebridgeMessenger", 110 | "name": "Messenger", 111 | "services": { 112 | "ifttt": { 113 | "key": "your_key" 114 | }, 115 | }, 116 | "messages": [ 117 | { 118 | "type": "ifttt", 119 | "name": "Test IFTTT", 120 | "event": "my_webhook", 121 | "value1": "hello world", 122 | "value2": "foo bar", 123 | "value3": "chewbacca" 124 | } 125 | ] 126 | } 127 | ] 128 | ``` 129 | * Service properties : 130 | * key *(required)*: Your key. To obtain your key, log into your IFTTT account and click on the Documentation link in the [Webhooks service](https://ifttt.com/maker_webhooks). 131 | * Message properties : 132 | * type *(required)*: Must be `ifttt`. 133 | * name *(required)*: Name of the switch. This will be **not** passed to IFTTT. 134 | * event *(required)*: Name of your event configured in IFTTT (Webhooks service). 135 | * value1 *(facultative)*: Value 1 to be passed to IFTTT. 136 | * value2 *(facultative)*: Value 2 to be passed to IFTTT. 137 | * value3 *(facultative)*: Value 3 to be passed to IFTTT. 138 | 139 | ### Email 140 | ``` 141 | "accessories": [ 142 | { 143 | "accessory": "HomebridgeMessenger", 144 | "name": "Messenger", 145 | "services": { 146 | "email": { 147 | "recipient": "you_email@domain.com", 148 | "smtpServer": "smtp.domain.com", 149 | "smtpPort": 465, 150 | "smtpSecure": true, 151 | "smtpUsername": "your_username", 152 | "smtpPassword": "your_password" 153 | } 154 | }, 155 | "messages": [ 156 | { 157 | "type": "email", 158 | "name": "Test email", 159 | "text": "This is a test", 160 | "recipients": "your_friend@domain.com, other_email@domain.com" 161 | } 162 | ] 163 | } 164 | ] 165 | ``` 166 | * Service properties : 167 | * recipient *(required)*: Default email address of the recipient. 168 | * smtpServer *(required)*: Address of the SMTP host. 169 | * smtpPort *(facultative)*: Port to connect to. (Default value is `25`). 170 | * smtpSecure *(facultative)*: Set to `true` if SMTP supports TLS. (Default value is `false`). 171 | * smtpUsername *(facultative)*: Username for the SMTP server, if required. 172 | * smtpPassword *(facultative)*: Password for the SMTP server, if required. 173 | * Message properties : 174 | * type *(required)*: Must be `email`. 175 | * name *(required)*: Name of the switch and subject of your email. 176 | * text *(required)*: Body of the email. 177 | * recipients *(facultative)*: Address of the recipients for this email. Multiple emails can be used, divided by a comma. If empty, the default email address at the service level will be used. 178 | 179 | 180 | ### Advanced configuration 181 | An example featuring **all technologies** is available [here](configuration-examples/advanced.example.json). 182 | 183 | ## Coming next 184 | * Support for [Pushcut](https://www.pushcut.io/) ([#5](https://github.com/potrudeau/homebridge-messenger/issues/5)) 185 | * Support for [Pushbullet](https://www.pushbullet.com) 186 | 187 | ## Change Log 188 | Available [here](CHANGELOG.md) 189 | 190 | ## Credits 191 | * [qbit/node-pushover](https://github.com/qbit/node-pushover) - library to send Pushover messages 192 | * [jeroentvb/IFTTT-webhooks-channel](https://github.com/jeroentvb/IFTTT-webhooks-channel) - library to send IFTTT messages 193 | * [nodemailer](https://github.com/nodemailer/nodemailer) - library to send SMTP emails 194 | 195 | ## License 196 | The [homebridge-messenger](https://github.com/potrudeau/homebridge-messenger) plugin is released under the [MIT license](LICENSE). 197 | -------------------------------------------------------------------------------- /config.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "pluginAlias": "HomebridgeMessenger", 3 | "pluginType": "accessory", 4 | "singular": false, 5 | "headerDisplay": "Homebridge plugin which allow users to send messages from HomeKit", 6 | "footerDisplay": "For more details on this plugin, see: https://github.com/potrudeau/homebridge-messenger", 7 | "schema": { 8 | "type": "object", 9 | "properties": { 10 | "name": { 11 | "title": "Name", 12 | "type": "string", 13 | "default": "Messenger", 14 | "required": true 15 | }, 16 | "services": { 17 | "title": "", 18 | "type": "object", 19 | "properties": { 20 | "pushover": { 21 | "title": "Pushover", 22 | "type": "object", 23 | "properties": { 24 | "user": { 25 | "title": "User", 26 | "type": "string", 27 | "required": true 28 | }, 29 | "token": { 30 | "title": "Token", 31 | "type": "string", 32 | "required": true 33 | } 34 | } 35 | }, 36 | "ifttt": { 37 | "title": "Pushover", 38 | "type": "object", 39 | "properties": { 40 | "key": { 41 | "title": "Key", 42 | "type": "string", 43 | "required": true 44 | } 45 | } 46 | }, 47 | "email": { 48 | "title": "Email", 49 | "type": "object", 50 | "properties": { 51 | "recipient": { 52 | "title": "Recipient", 53 | "type": "string", 54 | "required": true, 55 | "format": "email" 56 | }, 57 | "smtpServer": { 58 | "title": "SMTP Server", 59 | "type": "string", 60 | "required": true, 61 | "format": "hostname" 62 | }, 63 | "smtpPort": { 64 | "title": "SMTP Port", 65 | "type": "integer", 66 | "default": 8080, 67 | "maximum": 65535, 68 | "required": true 69 | }, 70 | "smtpSecure": { 71 | "title": "Secure", 72 | "type": "boolean" 73 | }, 74 | "smtpUsername": { 75 | "title": "SMTP Username", 76 | "type": "string" 77 | }, 78 | "smtpPassword": { 79 | "title": "SMTP Password", 80 | "type": "string" 81 | } 82 | } 83 | } 84 | } 85 | }, 86 | "messages": { 87 | "title": "Messages", 88 | "type": "array", 89 | "items": { 90 | "type": "object", 91 | "properties": { 92 | "name": { 93 | "title": "Name", 94 | "type": "string", 95 | "default": "", 96 | "required": true 97 | }, 98 | "type": { 99 | "title": "Type", 100 | "type": "string", 101 | "default": "pushover", 102 | "required": true, 103 | "oneOf": [ 104 | { 105 | "title": "Pushover", 106 | "enum": [ 107 | "pushover" 108 | ] 109 | }, 110 | { 111 | "title": "Email", 112 | "enum": [ 113 | "email" 114 | ] 115 | }, 116 | { 117 | "title": "IFTTT", 118 | "enum": [ 119 | "ifttt" 120 | ] 121 | } 122 | ] 123 | }, 124 | "text": { 125 | "title": "Text", 126 | "type": "string", 127 | "default": "", 128 | "required": true 129 | }, 130 | "event": { 131 | "title": "Event", 132 | "type": "string", 133 | "default": "", 134 | "required": true 135 | }, 136 | "value1": { 137 | "title": "Value 1", 138 | "type": "string", 139 | "default": "", 140 | "required": true 141 | }, 142 | "value2": { 143 | "title": "Value 2", 144 | "type": "string", 145 | "default": "", 146 | "required": true 147 | }, 148 | "value3": { 149 | "title": "Value 3", 150 | "type": "string", 151 | "default": "", 152 | "required": true 153 | }, 154 | "recipients": { 155 | "title": "Recipients", 156 | "type": "string", 157 | "default": "", 158 | "required": false 159 | }, 160 | "device": { 161 | "title": "Device", 162 | "type": "string", 163 | "default": "", 164 | "required": false 165 | }, 166 | "priority": { 167 | "title": "Priority", 168 | "type": "integer", 169 | "default": 0, 170 | "required": true, 171 | "oneOf": [ 172 | { 173 | "title": "Lowest Priority (-2)", 174 | "enum": [ 175 | -2 176 | ] 177 | }, 178 | { 179 | "title": "Low Priority (-1)", 180 | "enum": [ 181 | -1 182 | ] 183 | }, 184 | { 185 | "title": "Normal Priority (0)", 186 | "enum": [ 187 | 0 188 | ] 189 | }, 190 | { 191 | "title": "High Priority (1)", 192 | "enum": [ 193 | 1 194 | ] 195 | }, 196 | { 197 | "title": "Emergency Priority (2)", 198 | "enum": [ 199 | 2 200 | ] 201 | } 202 | ] 203 | }, 204 | "sound": { 205 | "title": "Sound", 206 | "type": "string", 207 | "default": "pushover", 208 | "oneOf": [ 209 | { 210 | "title": "pushover", 211 | "enum": [ 212 | "pushover" 213 | ] 214 | }, 215 | { 216 | "title": "bike", 217 | "enum": [ 218 | "bike" 219 | ] 220 | }, 221 | { 222 | "title": "bugle", 223 | "enum": [ 224 | "bugle" 225 | ] 226 | }, 227 | { 228 | "title": "cashregister", 229 | "enum": [ 230 | "cashregister" 231 | ] 232 | }, 233 | { 234 | "title": "classical", 235 | "enum": [ 236 | "classical" 237 | ] 238 | }, 239 | { 240 | "title": "cosmic", 241 | "enum": [ 242 | "cosmic" 243 | ] 244 | }, 245 | { 246 | "title": "falling", 247 | "enum": [ 248 | "falling" 249 | ] 250 | }, 251 | { 252 | "title": "gamelan", 253 | "enum": [ 254 | "gamelan" 255 | ] 256 | }, 257 | { 258 | "title": "incoming", 259 | "enum": [ 260 | "incoming" 261 | ] 262 | }, 263 | { 264 | "title": "intermission", 265 | "enum": [ 266 | "intermission" 267 | ] 268 | }, 269 | { 270 | "title": "magic", 271 | "enum": [ 272 | "magic" 273 | ] 274 | }, 275 | { 276 | "title": "mechanical", 277 | "enum": [ 278 | "mechanical" 279 | ] 280 | }, 281 | { 282 | "title": "pianobar", 283 | "enum": [ 284 | "pianobar" 285 | ] 286 | }, 287 | { 288 | "title": "siren", 289 | "enum": [ 290 | "siren" 291 | ] 292 | }, 293 | { 294 | "title": "spacealarm", 295 | "enum": [ 296 | "spacealarm" 297 | ] 298 | }, 299 | { 300 | "title": "tugboat", 301 | "enum": [ 302 | "tugboat" 303 | ] 304 | }, 305 | { 306 | "title": "alien", 307 | "enum": [ 308 | "alien" 309 | ] 310 | }, 311 | { 312 | "title": "climb", 313 | "enum": [ 314 | "climb" 315 | ] 316 | }, 317 | { 318 | "title": "persistent", 319 | "enum": [ 320 | "persistent" 321 | ] 322 | }, 323 | { 324 | "title": "echo", 325 | "enum": [ 326 | "echo" 327 | ] 328 | }, 329 | { 330 | "title": "updown", 331 | "enum": [ 332 | "updown" 333 | ] 334 | }, 335 | { 336 | "title": "vibrate", 337 | "enum": [ 338 | "vibrate" 339 | ] 340 | }, 341 | { 342 | "title": "none", 343 | "enum": [ 344 | "none" 345 | ] 346 | } 347 | ] 348 | }, 349 | "url": { 350 | "title": "Url", 351 | "type": "string", 352 | "default": "", 353 | "required": false, 354 | "format": "hostname" 355 | }, 356 | "urltitle": { 357 | "title": "Url Title", 358 | "type": "string", 359 | "default": "", 360 | "required": false 361 | } 362 | } 363 | } 364 | } 365 | } 366 | }, 367 | "layout": [ 368 | { 369 | "type": "fieldset", 370 | "expandable": true, 371 | "expanded": true, 372 | "title": "Main Switch Settings", 373 | "items": [ 374 | { 375 | "type": "div", 376 | "notitle": true, 377 | "displayFlex": true, 378 | "flex-direction": "row", 379 | "flex-wrap": "wrap", 380 | "items": [ 381 | { 382 | "key": "name", 383 | "flex-basis": "100%" 384 | } 385 | ] 386 | } 387 | ] 388 | }, 389 | { 390 | "type": "fieldset", 391 | "expandable": true, 392 | "title": "Pushover Settings", 393 | "items": [ 394 | { 395 | "type": "div", 396 | "notitle": true, 397 | "displayFlex": true, 398 | "flex-direction": "row", 399 | "flex-wrap": "wrap", 400 | "items": [ 401 | { 402 | "key": "services.pushover.user", 403 | "flex-basis": "50%" 404 | }, 405 | { 406 | "key": "services.pushover.token", 407 | "flex-basis": "50%" 408 | } 409 | ] 410 | } 411 | ] 412 | }, 413 | { 414 | "type": "fieldset", 415 | "expandable": true, 416 | "title": "IFTTT Settings", 417 | "items": [ 418 | { 419 | "type": "div", 420 | "notitle": true, 421 | "displayFlex": true, 422 | "flex-direction": "row", 423 | "flex-wrap": "wrap", 424 | "items": [ 425 | { 426 | "key": "services.ifttt.key", 427 | "flex-basis": "50%" 428 | } 429 | ] 430 | } 431 | ] 432 | }, 433 | { 434 | "type": "fieldset", 435 | "expandable": true, 436 | "title": "Email Settings", 437 | "items": [ 438 | { 439 | "type": "div", 440 | "notitle": true, 441 | "displayFlex": true, 442 | "flex-direction": "row", 443 | "flex-wrap": "wrap", 444 | "items": [ 445 | { 446 | "key": "services.email.recipient", 447 | "placeholder": "you@domain.com", 448 | "flex-basis": "50%" 449 | }, 450 | { 451 | "key": "services.email.smtpServer", 452 | "flex-basis": "50%" 453 | }, 454 | { 455 | "key": "services.email.smtpPort", 456 | "flex-basis": "50%" 457 | }, 458 | { 459 | "key": "services.email.smtpSecure", 460 | "flex-basis": "50%" 461 | }, 462 | { 463 | "key": "services.email.smtpUsername", 464 | "flex-basis": "50%" 465 | }, 466 | { 467 | "key": "services.email.smtpPassword", 468 | "flex-basis": "50%" 469 | } 470 | ] 471 | } 472 | ] 473 | }, 474 | { 475 | "key": "messages", 476 | "type": "array", 477 | "expandable": true, 478 | "title": "Messages", 479 | "items": [ 480 | { 481 | "type": "div", 482 | "notitle": true, 483 | "displayFlex": true, 484 | "flex-direction": "row", 485 | "flex-wrap": "wrap", 486 | "items": [ 487 | { 488 | "key": "messages[].type", 489 | "flex-basis": "100%" 490 | }, 491 | { 492 | "key": "messages[].name", 493 | "flex-basis": "100%" 494 | }, 495 | { 496 | "key": "messages[].text", 497 | "flex-basis": "100%", 498 | "condition": "messages[arrayIndex].type!=ifttt" 499 | }, 500 | { 501 | "key": "messages[].priority", 502 | "flex-basis": "50%", 503 | "condition": "messages[arrayIndex].type==pushover" 504 | }, 505 | { 506 | "key": "messages[].sound", 507 | "flex-basis": "50%", 508 | "condition": "messages[arrayIndex].type==pushover" 509 | }, 510 | { 511 | "key": "messages[].device", 512 | "flex-basis": "100%", 513 | "condition": "messages[arrayIndex].type==pushover" 514 | }, 515 | { 516 | "key": "messages[].url", 517 | "flex-basis": "50%", 518 | "condition": "messages[arrayIndex].type==pushover" 519 | }, 520 | { 521 | "key": "messages[].urltitle", 522 | "flex-basis": "50%", 523 | "condition": "messages[arrayIndex].type==pushover" 524 | }, 525 | { 526 | "key": "messages[].event", 527 | "flex-basis": "100%", 528 | "condition": "messages[arrayIndex].type==ifttt" 529 | }, 530 | { 531 | "key": "messages[].value1", 532 | "flex-basis": "33%%", 533 | "condition": "messages[arrayIndex].type==ifttt" 534 | }, 535 | { 536 | "key": "messages[].value2", 537 | "flex-basis": "33%", 538 | "condition": "messages[arrayIndex].type==ifttt" 539 | }, 540 | { 541 | "key": "messages[].value3", 542 | "flex-basis": "33%", 543 | "condition": "messages[arrayIndex].type==ifttt" 544 | }, 545 | { 546 | "key": "messages[].recipients", 547 | "flex-basis": "100%", 548 | "condition": "messages[arrayIndex].type==email" 549 | } 550 | ] 551 | } 552 | ] 553 | } 554 | ] 555 | } --------------------------------------------------------------------------------