001package com.pi4j.io.gpio.trigger; 002 003/* 004 * #%L 005 * ********************************************************************** 006 * ORGANIZATION : Pi4J 007 * PROJECT : Pi4J :: Java Library (Core) 008 * FILENAME : IFTTTMakerChannelTrigger.java 009 * 010 * This file is part of the Pi4J project. More information about 011 * this project can be found here: https://www.pi4j.com/ 012 * ********************************************************************** 013 * %% 014 * Copyright (C) 2012 - 2021 Pi4J 015 * %% 016 * This program is free software: you can redistribute it and/or modify 017 * it under the terms of the GNU Lesser General Public License as 018 * published by the Free Software Foundation, either version 3 of the 019 * License, or (at your option) any later version. 020 * 021 * This program is distributed in the hope that it will be useful, 022 * but WITHOUT ANY WARRANTY; without even the implied warranty of 023 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 024 * GNU General Lesser Public License for more details. 025 * 026 * You should have received a copy of the GNU General Lesser Public 027 * License along with this program. If not, see 028 * <http://www.gnu.org/licenses/lgpl-3.0.html>. 029 * #L% 030 */ 031 032 033import com.pi4j.io.gpio.GpioPin; 034import com.pi4j.io.gpio.PinState; 035import com.pi4j.io.gpio.event.IFTTTMakerChannelTriggerEvent; 036import com.pi4j.io.gpio.event.IFTTTMakerChannelTriggerListener; 037 038import javax.net.ssl.HttpsURLConnection; 039import java.io.*; 040import java.net.*; 041import java.nio.charset.StandardCharsets; 042import java.text.DateFormat; 043import java.text.SimpleDateFormat; 044import java.util.Date; 045import java.util.List; 046 047@SuppressWarnings("unused") 048public class IFTTTMakerChannelTrigger extends GpioTriggerBase { 049 050 private final String apikey; 051 private final String eventName; 052 private IFTTTMakerChannelTriggerListener listener = null; 053 054 protected static final String IFTTT_MAKER_CHANNEL_URL = "https://maker.ifttt.com/trigger/%s/with/key/%s"; 055 056 public IFTTTMakerChannelTrigger(String apikey, String eventName) { 057 super(); 058 this.apikey = apikey; 059 this.eventName = eventName; 060 } 061 062 public IFTTTMakerChannelTrigger(String apikey, String eventName, PinState state) { 063 super(state); 064 this.apikey = apikey; 065 this.eventName = eventName; 066 } 067 068 public IFTTTMakerChannelTrigger(String apikey, String eventName, PinState[] states) { 069 super(states); 070 this.apikey = apikey; 071 this.eventName = eventName; 072 } 073 074 public IFTTTMakerChannelTrigger(String apikey, String eventName, List<PinState> states) { 075 super(states); 076 this.apikey = apikey; 077 this.eventName = eventName; 078 } 079 080 public IFTTTMakerChannelTrigger(String apikey, String eventName, IFTTTMakerChannelTriggerListener listener) { 081 super(); 082 this.apikey = apikey; 083 this.eventName = eventName; 084 this.listener = listener; 085 } 086 087 public IFTTTMakerChannelTrigger(String apikey, String eventName, PinState state, IFTTTMakerChannelTriggerListener listener) { 088 super(state); 089 this.apikey = apikey; 090 this.eventName = eventName; 091 this.listener = listener; 092 } 093 094 public IFTTTMakerChannelTrigger(String apikey, String eventName, PinState[] states, IFTTTMakerChannelTriggerListener listener) { 095 super(states); 096 this.apikey = apikey; 097 this.eventName = eventName; 098 this.listener = listener; 099 } 100 101 public IFTTTMakerChannelTrigger(String apikey, String eventName, List<PinState> states, IFTTTMakerChannelTriggerListener listener) { 102 super(states); 103 this.apikey = apikey; 104 this.eventName = eventName; 105 this.listener = listener; 106 } 107 108 /** 109 * Use this method to register/assign a custom trigger listener. 110 * 111 * This callback listener is fired when the IFTTTMakerChannelTrigger 112 * receives a GPIO pin change that invokes the trigger. This listener 113 * provides the consumer an opportunity to be notified of the trigger event, 114 * to optionally abort the triggered event before sending data to the IFTTT 115 * Maker Channel API, or an opportunity to override any data payload values 116 * before transmitting to the IFTTT Maker Channel API. 117 * 118 * @param listener 119 */ 120 public void setTriggerListener(IFTTTMakerChannelTriggerListener listener){ 121 this.listener = listener; 122 } 123 124 @Override 125 public void invoke(GpioPin pin, PinState state) { 126 try { 127 // create an ISO-8601 complaint date format for timestamp 128 DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); 129 130 // create the default JSON payload that will get supplied as the default 131 // data for 'value3' in the IFTTT Maker Channel trigger API 132 String json = "{\"pin\":{\"name\":\"" + pin.getName() + 133 "\",\"address\":\"" + pin.getPin().getAddress() + 134 "\",\"provider\":\"" + pin.getPin().getProvider() + 135 "\",\"mode\":\"" + pin.getMode().getName() + 136 "\",\"direction\":\"" + pin.getMode().getDirection() + 137 "\",\"pull\":\"" + pin.getPullResistance().getName() + 138 "\"},\"state\":{" + 139 "\"name\":\"" + state.getName() + 140 "\",\"value\":\"" + state.getValue() + 141 "\",\"is-high\":\"" + state.isHigh() + 142 "\",\"is-low\":\"" + state.isLow() + 143 "\"},\"timestamp\":\"" + df.format(new Date()) + "\"}"; 144 145 // create an IFTTT Maker Channel event 146 IFTTTMakerChannelTriggerEvent event = new IFTTTMakerChannelTriggerEvent( 147 this, // IFTTTMakerChannelTrigger instance 148 pin, // GPIO PIN instance 149 state, // GPIO PIN STATE 150 eventName, // IFTTT EVENT NAME 151 pin.getName(), // VALUE 1 152 Integer.toString(state.getValue()), // VALUE 2 153 json); // VALUE 3 154 155 // if the consumer configured a custom listener for callback events, 156 // then we need to invoke the listner instance 'onTriggered' callback 157 // method with the IFTTTMakerChannelTriggerEvent instance. 158 if(this.listener != null){ 159 if(this.listener.onTriggered(event) == false) { 160 // abort 161 return; 162 } 163 } 164 165 // we must URL encode the IFTTT payload data values (value1, value2, value3) 166 String value1 = URLEncoder.encode(event.getValue1(), StandardCharsets.UTF_8.name()); 167 String value2 = URLEncoder.encode(event.getValue2(), StandardCharsets.UTF_8.name()); 168 String value3 = URLEncoder.encode(event.getValue3(), StandardCharsets.UTF_8.name()); 169 170 // create the URL parameters and post data 171 String urlParameters = "value1=" + value1 + "&value2=" + value2 + "&value3=" + value3; 172 byte[] postData = urlParameters.getBytes(StandardCharsets.UTF_8); 173 174 // create the IFTTT Maker Channel trigger URL & connection 175 URL url = new URL(String.format(IFTTT_MAKER_CHANNEL_URL, eventName, apikey)); 176 HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); 177 connection.setRequestMethod("POST"); 178 connection.setDoOutput(true); 179 connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 180 connection.setRequestProperty("charset", "utf-8"); 181 connection.setRequestProperty("Content-Length", Integer.toString(postData.length)); 182 connection.setUseCaches(false); 183 try( DataOutputStream stream = new DataOutputStream(connection.getOutputStream())) { 184 stream.write( postData ); 185 } 186 187 // get HTTP response 188 int responseCode = connection.getResponseCode(); 189 190 // if the HTTP post resulted in an error, then raise an exception 191 if(responseCode != 200) { 192 System.err.println("IFTTT MakerChannel ERROR Response Code: " + responseCode); 193 BufferedReader in = new BufferedReader( new InputStreamReader(connection.getInputStream())); 194 String inputLine; 195 StringBuffer response = new StringBuffer(); 196 while ((inputLine = in.readLine()) != null) { 197 response.append(inputLine); 198 } 199 in.close(); 200 throw new RuntimeException(response.toString()); 201 } 202 } 203 catch (IOException e) { 204 throw new RuntimeException(e); 205 } 206 } 207}