<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Brandon McFarlin</title>
    <description>The latest articles on DEV Community by Brandon McFarlin (@brandawg93).</description>
    <link>https://dev.to/brandawg93</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1218282%2F46d5c72f-4293-47cd-a9f6-bc294dbd615d.jpeg</url>
      <title>DEV Community: Brandon McFarlin</title>
      <link>https://dev.to/brandawg93</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/brandawg93"/>
    <language>en</language>
    <item>
      <title>Client, API, and database. A full-stack web app built entirely in Typescript.</title>
      <dc:creator>Brandon McFarlin</dc:creator>
      <pubDate>Thu, 30 Nov 2023 15:08:02 +0000</pubDate>
      <link>https://dev.to/brandawg93/client-api-and-database-a-full-stack-web-app-built-entirely-in-typescript-2gm0</link>
      <guid>https://dev.to/brandawg93/client-api-and-database-a-full-stack-web-app-built-entirely-in-typescript-2gm0</guid>
      <description>&lt;p&gt;Many web applications of the past required knowledge of multiple languages and architectures to stand up a fully working service. But newer languages have emerged allowing developers to consolidate their knowledge to a single language. Let’s take a look at the technologies provided in Typescript that will allow us to create a client using &lt;a href="https://react.dev/"&gt;React&lt;/a&gt;, an API using &lt;a href="https://expressjs.com/"&gt;ExpressJS&lt;/a&gt;, and a database using &lt;a href="https://sequelize.org/"&gt;Sequelize&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt;: The complete code can be found on &lt;a href="https://github.com/Brandawg93/typescript-template"&gt;Github&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  API
&lt;/h2&gt;

&lt;p&gt;Let’s start with the API. Why? Because it’s the backbone of our application of course! And more importantly, the structure will make sense as we use predefined setup scripts to create the project.&lt;/p&gt;

&lt;p&gt;Let’s begin by initializing the ExpressJS project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir typescript-template
cd typescript-template
npm init -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a basic &lt;code&gt;package.json&lt;/code&gt; file that we will be able to modify later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "typescript-template",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" &amp;amp;&amp;amp; exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here, let’s begin installing the necessary packages required to run ExpressJS and typescript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save cors express dotenv
npm install --save-dev @types/cors @types/express @types/node concurrently nodemon typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will install the required packages and add them to our &lt;code&gt;package.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Next let’s create our &lt;code&gt;tsconfig.json&lt;/code&gt; file to tell typescript how we want our code compiled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "compilerOptions": {
    "target": "es2016",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    "module": "commonjs",                                /* Specify what module code is generated. */
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */
    "strict": true,                                      /* Enable all strict type-checking options. */
    "skipLibCheck": true,                                /* Skip type checking all .d.ts files. */
    "rootDir": "server",
    "outDir": "dist",
  },
  "include": [
    "server"
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most of these options are defaults if you run &lt;code&gt;tsc --init&lt;/code&gt; with the notable exception of &lt;code&gt;“rootDir”&lt;/code&gt;, &lt;code&gt;“outDir”&lt;/code&gt;, and the &lt;code&gt;“include”&lt;/code&gt; section. &lt;code&gt;“rootDir”&lt;/code&gt; will tell typescript where our application code is, &lt;code&gt;“outDir”&lt;/code&gt; will tell typescript where to compile the code, and &lt;code&gt;“include”&lt;/code&gt; will tell typescript to only include the files within our specified folder (i.e. don’t include the client folder that we will make later).&lt;/p&gt;

&lt;p&gt;Now that typescript knows what to do and where to look for our code, let’s create that &lt;code&gt;server&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within this folder, let’s create a simple express file called &lt;code&gt;server.ts&lt;/code&gt; and add some basic logic for starting express.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import express, { Express, Request, Response } from 'express';
import dotenv from 'dotenv';
import path from 'path'; // We will use this later
import cors from 'cors'; // We will use this later

dotenv.config();

const app: Express = express();
const port = process.env.WEB_PORT || 8080;

app.use(express.json());

app.get('/ping', (req: Request, res: Response) =&amp;gt; {
  res.send('pong');
});

app.listen(port, async () =&amp;gt; {
  console.log(`⚡️[typescript-template]: Server is running at http://localhost:${port}`);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sets up a simple express application on port 8080 that will reply &lt;code&gt;pong&lt;/code&gt; to our &lt;code&gt;/ping&lt;/code&gt; request. Before trying it out, let’s add some scripts to our &lt;code&gt;package.json&lt;/code&gt; to make building and deploying a bit easier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "typescript-template",
  "version": "1.0.0",
  "description": "",
  "main": "dist/server.js",
  "scripts": {
    "clean": "rm -rf dist",
    "server": "concurrently \"tsc --watch\" \"nodemon server/**/*\"",
    "build": "tsc &amp;amp;&amp;amp; npm run build --prefix client",
    "start": "node dist/server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "cors": "^2.8.5",
    "dotenv": "^16.3.1",
    "express": "^4.18.2"
  },
  "devDependencies": {
    "@types/cors": "^2.8.16",
    "@types/express": "^4.17.21",
    "@types/node": "^20.9.2",
    "concurrently": "^8.2.2",
    "nodemon": "^3.0.1",
    "typescript": "^5.2.2"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we update our &lt;code&gt;"main”&lt;/code&gt; value to &lt;code&gt;dist/server.js&lt;/code&gt; Then we add the necessary scripts. The &lt;code&gt;clean&lt;/code&gt; script removes build files, the &lt;code&gt;server&lt;/code&gt; script starts a development run of the application that will automatically rebuild as files are changed, the &lt;code&gt;build&lt;/code&gt; script builds the project for production, and finally, the &lt;code&gt;start&lt;/code&gt; script starts the application from the files from the production build. So typically, you would want to develop with &lt;code&gt;server&lt;/code&gt; and deploy to production and use &lt;code&gt;start&lt;/code&gt;. With all that done, let’s give it a shot!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, simply navigate to &lt;a href="http://localhost/ping"&gt;http://localhost/ping&lt;/a&gt; in your browser. You should see &lt;code&gt;pong&lt;/code&gt;. If so, yay! Your server is set up. If not, check the console to see any errors you may have.&lt;/p&gt;




&lt;h2&gt;
  
  
  Client
&lt;/h2&gt;

&lt;p&gt;Next, let’s work on our client code. For this, we will be using react’s standard startup script. We will also install everything inside a &lt;code&gt;client&lt;/code&gt; folder to keep this code separate from the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app client --template typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate all of the basic code needed to start up our client with no modifications required! Well…not yet anyways. Let’s run the client to see it in action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd client
npm run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now see a basic react app telling you to edit &lt;code&gt;App.tsx&lt;/code&gt;. If so, awesome! If not, check the logs for any errors.&lt;/p&gt;

&lt;p&gt;Now, let’s hook up the client and server. First let’s create a new api endpoint in the server that displays the current time from the server. Add the following in &lt;code&gt;server.ts&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.get('/api/time', (req: Request, res: Response) =&amp;gt; {
  res.send(JSON.stringify({ time: new Date()}));
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let’s set up the client to call this endpoint. Modify &lt;code&gt;App.tsx&lt;/code&gt; to look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';
import logo from './logo.svg';
import './App.css';

const getTime = async () =&amp;gt; {
  const response = await fetch('/api/time');
  const body = await response.json();
  if (response.status !== 200) {
    throw Error(body.message);
  }
  return body;
}

function App() {
  const [time, setTime] = React.useState('');
  React.useEffect(() =&amp;gt; {
    getTime()
      .then(res =&amp;gt; setTime(res.time))
      .catch(err =&amp;gt; console.log(err));
  }, []);
  return (
    &amp;lt;div className="App"&amp;gt;
      &amp;lt;header className="App-header"&amp;gt;
        &amp;lt;img src={logo} className="App-logo" alt="logo" /&amp;gt;
        &amp;lt;p&amp;gt;{new Date(time).toLocaleDateString()}&amp;amp;nbsp;{new Date(time).toLocaleTimeString()}&amp;lt;/p&amp;gt;
      &amp;lt;/header&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code, we added a &lt;code&gt;getTime&lt;/code&gt; method that calls the same endpoint and displays it on the page. Let’s try it! Start the client and server in two different shells and refresh the page. (We will make this easier later I promise!)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd client
npm run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it worked perfectly right? Wrong… What does your browser say? Probably &lt;code&gt;Cannot GET /&lt;/code&gt;. We’ve hooked up the api call, but we forgot to hook up the actual files created by the client to be used by the server. Let’s fix that now. We will need to make a few modifications.&lt;/p&gt;

&lt;p&gt;First, we need to point the express server to the build files created by react. Add this to your &lt;code&gt;server.ts&lt;/code&gt; just below &lt;code&gt;app.use(express.json());&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.use(express.static(path.join(__dirname, '../client/build')));
app.get('/', (req, res) =&amp;gt; {
  res.sendFile(path.join(__dirname, '../client/build/index.html'));
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also need to make sure the server is happy with requests coming from a different base uri since different ports (e.g. 8080 and 3000) can cause a CORS issue. Add the following before what we just added above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (process.env.NODE_ENV !== 'production') {
  app.use(cors());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we need to make sure the client will proxy requests from port 3000 to port 8080. Let’s add the following to our CLIENT’S &lt;code&gt;package.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"proxy": "http://localhost:8080",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, let’s make our scripts a bit more convenient so we don’t have to run multiple shells. Add the following to your SERVER’S &lt;code&gt;package.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"client": "npm start --prefix client",
"dev": "concurrently \"npm run server\" \"npm run client\""
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, Let’s try it!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when you go to &lt;a href="http://localhost:3000"&gt;http://localhost:3000&lt;/a&gt;, you will see the date and time displayed on the page. Woohoo!&lt;/p&gt;




&lt;h2&gt;
  
  
  Database
&lt;/h2&gt;

&lt;p&gt;Finally let’s create a database. I will be using &lt;a href="https://sequelize.org/"&gt;Sequelize&lt;/a&gt; to create a simple database in memory, but other options can be used such as sqlite or mysql. For this tutorial, let’s create a database that stores the last time the server is accessed and can retrieve the latest access time. Let’s begin by installing the necessary packages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save sequelize sqlite3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that more packages may be needed if you decide to use a different database. Now let’s create a file called &lt;code&gt;database.ts&lt;/code&gt; within our &lt;code&gt;server&lt;/code&gt; folder and initialize it with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Sequelize, Model, DataTypes } from 'sequelize';

class ServerAccess extends Model {
  public id!: number;
  public lastAccessed!: Date;
}

const sequelize = new Sequelize('sqlite::memory:');


ServerAccess.init(
  {
    id: {
      type: DataTypes.INTEGER,
      autoIncrement: true,
      primaryKey: true,
    },
    lastAccessed: {
      type: DataTypes.DATE,
      allowNull: false,
    },
  },
  {
    sequelize,
    modelName: 'ServerAccess',
  }
);

sequelize.sync();

export async function updateLastAccessed(): Promise&amp;lt;void&amp;gt; {
  const serverAccess = await ServerAccess.findOne();
  if (serverAccess) {
    serverAccess.lastAccessed = new Date();
    await serverAccess.save();
  } else {
    await ServerAccess.create({ lastAccessed: new Date() });
  }
}

export async function getLastAccessed(): Promise&amp;lt;Date | null&amp;gt; {
  const serverAccess = await ServerAccess.findOne();
  return serverAccess ? serverAccess.lastAccessed : null;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And…well…That’s pretty much it! We’ve just told Sequelize to create a database with an auto incrementing id and the last access time. We also have two exported functions to update and get that time. Simple!&lt;/p&gt;




&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;Now that we have the client, API, and database working, let’s put all that we’ve learned together by displaying the last time the server was accessed (via &lt;code&gt;/api/time&lt;/code&gt;) on our react client app. First, let’s modify the server to retrieve the last access time AND THEN update the access time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.get('/api/time', async (req: Request, res: Response) =&amp;gt; {
  const lastAccessed = (await getLastAccessed()) || new Date();
  res.send(JSON.stringify({ time: new Date(), lastAccessed: new Date(lastAccessed) }));
  await updateLastAccessed();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these changes, we will now initialize &lt;code&gt;lastAccessed&lt;/code&gt; to either the last access time or the current datetime if the server has never been accessed. Once we send the response to the client, we update the last access time. Finally, let’s update the &lt;code&gt;App&lt;/code&gt; function within &lt;code&gt;App.tsx&lt;/code&gt; in the client to display the last access time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function App() {
  const [time, setTime] = React.useState('');
  const [lastAccessed, setLastAccessed] = React.useState('');
  React.useEffect(() =&amp;gt; {
    getTime()
      .then(res =&amp;gt; {
        setTime(res.time);
        setLastAccessed(res.lastAccessed);
      })
      .catch(err =&amp;gt; console.log(err));
  }, []);
  return (
    &amp;lt;div className="App"&amp;gt;
      &amp;lt;header className="App-header"&amp;gt;
        &amp;lt;img src={logo} className="App-logo" alt="logo" /&amp;gt;
        &amp;lt;label&amp;gt;Current Time:&amp;lt;/label&amp;gt;
        &amp;lt;p&amp;gt;{new Date(time).toLocaleDateString()}&amp;amp;nbsp;{new Date(time).toLocaleTimeString()}&amp;lt;/p&amp;gt;
        &amp;lt;label&amp;gt;Last Access Time:&amp;lt;/label&amp;gt;
        &amp;lt;p&amp;gt;{new Date(lastAccessed).toLocaleDateString()}&amp;amp;nbsp;{new Date(lastAccessed).toLocaleTimeString()}&amp;lt;/p&amp;gt;
      &amp;lt;/header&amp;gt;
    &amp;lt;/div&amp;gt;
  );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it! Simply run &lt;code&gt;npm run dev&lt;/code&gt; again and you should see the dates and times update accordingly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we learned to create a full-stack web application completely in Typescript! We utilized some great frameworks to get us started like &lt;a href="https://expressjs.com/"&gt;ExpressJS&lt;/a&gt;, &lt;a href="https://react.dev/"&gt;React&lt;/a&gt;, and &lt;a href="https://sequelize.org/"&gt;Sequelize&lt;/a&gt;. We are now able to use a small set of scripts to build, test, develop, and deploy our entire codebase.&lt;/p&gt;

&lt;p&gt;The complete code for this tutorial can be found on Github along with a few other tools like &lt;a href="https://eslint.org/"&gt;ESLint&lt;/a&gt; here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Brandawg93/typescript-template"&gt;https://github.com/Brandawg93/typescript-template&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>typescript</category>
      <category>react</category>
      <category>express</category>
    </item>
    <item>
      <title>How to get notified of newly connected devices on your OpenWRT router</title>
      <dc:creator>Brandon McFarlin</dc:creator>
      <pubDate>Fri, 24 Nov 2023 22:02:30 +0000</pubDate>
      <link>https://dev.to/brandawg93/how-to-get-notified-of-newly-connected-devices-on-your-openwrt-router-3jjf</link>
      <guid>https://dev.to/brandawg93/how-to-get-notified-of-newly-connected-devices-on-your-openwrt-router-3jjf</guid>
      <description>&lt;p&gt;So you’ve just set up OpenWRT with all the bells and whistles only to realize there is no out-of-the-box way to receive notifications for newly connected devices. No worries! With this tutorial, we will set up our OpenWRT server to send notifications to &lt;a href="https://pushover.net/" rel="noopener noreferrer"&gt;Pushover&lt;/a&gt; whenever a new device is connected to the server.&lt;/p&gt;




&lt;p&gt;Let’s start with Pushover. Sign up is really easy, and pricing is very reasonable. It’s typically a one-time purchase per device used. For myself, I’ve purchased it for my iPhone and really enjoy its simplicity. Once signed up, create a new application with your preferred options. Lastly, find your app’s api key along with your user key. They should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz9nsckx8g03daxombrk3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz9nsckx8g03daxombrk3.png" alt="API Key"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb13gh995h45uy1gohscp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb13gh995h45uy1gohscp.png" alt="User Key"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With pushover ready to go, let’s head back to our OpenWRT server. Create a new script file called &lt;code&gt;new_device_notification.sh&lt;/code&gt; with the following lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/sh

cat &amp;lt;&amp;lt; "EOF" &amp;gt; /etc/hotplug.d/dhcp/90-newdev
[ "$ACTION" == "add" ] || exit 0
# [ "$ACTION" == "add" -o "$ACTION" == "update" ] || exit 0
known_macs="/etc/known_macs"
user_key="your-user-key"
api_key="your-api-key"
if ! /bin/grep -iq "$MACADDR" "$known_macs"; then
  msg="New device detected:
MAC: $MACADDR
IP: $IPADDR
Hostname: $HOSTNAME"
  echo "$MACADDR $IPADDR $HOSTNAME" &amp;gt;&amp;gt; /etc/known_macs
  curl -s \
       --form-string "token=$api_key" \
       --form-string "user=$user_key" \
       --form-string "title=New Device" \
       --form-string "message=$msg" \
       https://api.pushover.net/1/messages.json
fi
exit 0
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;your-api-key&lt;/code&gt; and &lt;code&gt;your-user-key&lt;/code&gt; with the values provided from Pushover. This script will check for new devices on your DHCP server as these devices make connections. If the server has not seen it before, it will add the device to a list of known devices and send you a notification.&lt;/p&gt;

&lt;p&gt;Finally, let’s make this script runnable and execute it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chmod +x new_device_notification.sh
./new_device_notification.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s it! Simply restart your server and you should begin receiving messages from Pushover. You may receive many messages at the beginning as existing devices are added, but from then on, only new devices will trigger a message.&lt;/p&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;

</description>
      <category>openwrt</category>
      <category>pushover</category>
      <category>router</category>
      <category>bash</category>
    </item>
  </channel>
</rss>
