├── .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 |
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 | }
--------------------------------------------------------------------------------