Notes for some companies.

This commit is contained in:
Norm Rasmussen
2023-10-11 17:18:04 -04:00
parent 3ee215a5b5
commit 84ecd6c471
5 changed files with 460 additions and 1 deletions

View File

@ -122,4 +122,25 @@ In addition: activate or deactivate activities based on a property.
- Questions about Badges - he said he couldn't find them and Patrycja chuckled and let him know that its totally custom.
- Dirk has some designs, but only one was added to Figma.
- Internal discussions on the number of activities per course. Some DS people adding a ton of activities.
- Dirk wants to add time next to section or activities for estimated time to complete.
- Dirk wants to add time next to section or activities for estimated time to complete
## 10/10/2023
### Sender Domains, Milestones, and Custom Domain
| Record Type | URL | Value|
|----|----|----|
| CNAME | np._domainkey.datasnipper.com | np.domainkey.u1860655.wl156.sendgrid.net |
| CNAME | np2._domainkey.datasnipper.com | np2.domainkey.u1860655.wl156.sendgrid.net |
| TXT | datasnipper.com | northpass-domain-verification=9e3afad8d68c30cc9c50e8a02afda447 |
TODO: Add logo to Footer
Design:
- Grey-out badges should be more opaque/faded
- Fix Links in header
- Add Resources Description on button.
- Learner preview shows blank page - no header, footer, etc.
Candide to connect Norm to Gabriele Pipo & IT to help set up the custom domains for email and URL.

View File

@ -432,3 +432,12 @@ Erin to put in fast lane request to switch Cert name from G2 Core to G2 Fundamen
House keeping:
* Erin in Boston - October 17th & 18th.
## 10/10/2023
### Content sync - Erin
* Erin got message today about a 12th due date. Design team asked for the 16th.
* If she says no, they likely still won't deliver by the 12th.
* Potential delay, as long as EOM release, Kaitlin will be happy.
* TODO: Review the two draft courses - Core Onboarding & Review Growth Onboarding

View File

@ -708,3 +708,51 @@ TODO: Test if a course can be accessible outside of Northpass without it being a
* Sara is no longer working on the Spark side
* Frank is drowning in gif work and they need help
* Norm to ask if we can replace him.
## 10/10/2023
### Metrics Reporting Meeting
*Background:*
* Patrick Duffy - Drivers Ops
* "My Metrics" for driver metrics
* Spark was started 5 years ago.
* Currently revamping all of MyMetrics programs
* Want to understand how MyMetrics intersects with Resource Center
*What they want to see differently*
* Goal is to decrease number of clicks for Drivers
* App experience has brief description of every metric and the value
* Link that goes between individual metric
* Metrics include:
* Dropped Trips
* Accepted Offers
* Customer Rating
* On time Arrivals
* (Shopping Metrics) - Items Found
* "4 Total trips, Learn more about your metrics >" --> This would link to Resource Center
* Currently links to website
* Website is for prospective and new drivers, not active
* Want to link to RC, which is for existing drivers
* Current metrics will stay in the app. Those are not moving.
* They just want to show the definition of the metrics in the RC.
* They keep bringing up "landing pages". What else do they want on the landing pages?
* Two versions:
* Short term - expanded definition with metric rules and exceptions
* Long term - increased transparency, what went into their calculation.
* If 40%, what went into and affected the 40%.
* Possible Flows:
* MyMetrics > click "see definition" > [utilize context aware mechanism] click "Done" at bottom
* MyMetrics > click "see definition" > include box that says "click here to go back or click below to see other definitions."
* Issues:
* Major pain point is lack of understanding of MyMetrics
* Drivers complain about definitions of metrics
* Kaitlyn: a few avenues for solutions
* TODO: Ask Rob if Walmart engineering is needed for deep linking to activities. They claim they need to do that.
* How does Pat update MyMetrics page and content?
* Looking for a January release
* This would be a big overhaul
* They are providing avenues and solutions to leadership
* Ece to be managing front end and back end dependencies.

View File

@ -149,3 +149,9 @@ Thanks, Sophie, and Viky! Enjoy your vacation, Sophie! Talk to you when you retu
Best,
Norm
## 10/11/2023
### Sophie back from PTO

375
temp.md Normal file
View File

@ -0,0 +1,375 @@
---
title: 'Creating a BirdNetPi Dashboard in HomeAssistant - Part 1'
date: 2023-09-30T11:21:55-04:00
tags: ["homeassistant", "python", "diy"]
author: "Me"
showToc: true
TocOpen: false
draft: false
hidemeta: false
description: 'Learn how to take BirdNET-Pi Detections to create and display entities in HomeAssistant.'
disableHLJS: true # to disable highlightjs
disableShare: false
disableHLJS: false
hideSummary: false
searchHidden: true
ShowReadingTime: true
ShowBreadCrumbs: true
ShowPostNavLinks: true
ShowWordCount: true
ShowRssButtonInSectionTermList: true
UseHugoToc: true
cover:
image: "birdnet-homeassistant.png"
alt: "BirdNET-Pi and HomeAssistant: Happier together!"
caption: "Bring together your BirdNET-Pi setup and display a dashboard in HomeAssistant"
relative: false # when using page bundles set this to true
hidden: true # only hide on current single page
---
This is Part One of a Two Part Series. You can find Part Two, [here.]({{<ref "birdnet_homeassistant_part2.md" >}})
**Update: 10/11/2023. A huge thanks to Mastodon User [e_mobile2014](https://mastodon.social/@e_mobil2014) who found a broken link in this guide and pointed out that I never explained how to get the mqtt sensors into HomeAssistant!**
## What you will need
* [BirdNET-Pi](https://github.com/mcguirepr89/BirdNET-Pi)
* [HomeAssistant](https://www.home-assistant.io/)
* [AppDaemon](https://appdaemon.readthedocs.io/en/latest/)
* MQTT Broker (I use [Mosquitto](https://mosquitto.org/))
## Background
In early 2023, at the height of the [Raspberry Pi
shortage](https://www.tomshardware.com/news/raspberry-pi-availability-analysis) I felt like a king with an extra Rpi laying
around, not being used. I'm a big fan of any sort of passive intake of information and had been looking around for various
citizen science-style projects that can capture information from the world around me. Since I'm already running an ADS-B
antenna with [Flight Aware](https://www.flightaware.com/), I figured this next project would deal with radio
waves/transmissions. Instead, to my amazement, I discovered [BirdNET-Pi](https://github.com/mcguirepr89/BirdNET-Pi)!
## What is BirdNET-Pi?
In case you didn't click the links above, [BirdNET-Pi](https://github.com/mcguirepr89/BirdNET-Pi) is an app built specifically
made for Rapsberry Pi devices, that builds off the [BirdNET Framework](https://github.com/kahst/BirdNET-Analyzer). BirdNET is
one of the most advanced acoustic monitoring tools available for passively monitoring bird diversity populations.
Where BirdNET-Pi takes it to the next level is the ability to setup an SBC - hopefully enclosed in a waterproof space! - and
monitor birds in your local environment over time.
I think this project is beyond neat. It runs a bit slow on a Raspberry Pi 3, but overall it runs smoothly. I was even able to
[contribute a PR to the project](https://github.com/mcguirepr89/BirdNET-Pi/pull/821) in April when I noticed a bug in the
platform after a hard reset of my Pi.
## BirdNET-PI Notification Setup - MQTT
Once you have BirdNET-Pi up and running, you'll need to head over to the Settings and setup the correct MQTT payloads. Here
are the possible variables you can pass in an MQTT payload:
* `$sciname`: Scientific Name
* `$comname`: Common Name
* `$confidence`: Confidence Score
* `$confidencepct`: Confidence Score as a percentage (eg. 0.91 => 91)
* `$listenurl`: A link to the detection
* `$date`: Date
* `$time`: Time
* `$week`: Week
* `$latitude`: Latitude
* `$longitude`: Longitude
* `$cutoff`: Minimum Confidence set in "Advanced Settings"
* `$sens`: Sigmoid Sensitivity set in "Advanced Settings"
* `$overlap`: Overlap set in "Advanced Settings"
* `$flickrimage`: A preview image of the detected species from Flickr. Set your API key below.
For our purposes, we will only be using `$comname, $sciname, $date, $time, $week,` and `$confidence`. However, this entire
process is extremely customizable, which you'll learn more about in the AppDaemon section. Please expand on it and include
information that is pertinent to your own uses.
Here is how I've setup my MQTT payload from BirdNET-Pi Settings:
![Notification Settings](/posts/img/birdnet_mqtt_settings.png, "MQTT Settings in BirdNET-Pi")
Here it is in text form:
```none
Notification Title: $comname,
Notification Body: $sciname, $date, $time, $week, $confidence
[ ] Notify each new infrequent species detection (< 5 visits per week)
[ ] Notify each new species first detection of the day
[X] Notify each new detection
[X] Send weekly report
Minimum time between notifications of the same species (sec): 5
```
To test my MQTT notifications, I use the iOS client "MQTTool". After signing up, head to "Subscribe" and type `birdnet` as
the topic and then click Subscribe. If everything is setup correctly and there are birds being recorded by the BirdNET-Pi's
microphone, you should start seeing those detections in the MQTTool app. If so, fantastic news! Let's move onto AppDaemon.
## AppDaemon Script
Now that we have the Pi communicating via MQTT, it's time to get that information into HomeAssistant. I've shared [the full
script]({{<ref "birdnet_homeassistant.md#birdnet-appdaemon-script">}}) at the bottom of this page, but let's jump into each
section. This is not a full tutorial of how to use AppDaemon, but it may help fill in any knowledge gaps with the system.
### Imports
First, we're going to import `time` and `requests`. We're going to use time as a backup to the `$time` component in the
payload. This can be helpful to see if there delays, or if BirdNET-Pi stopped detecting. We're then going to use requests to
pull from Wikipedia's API and grab a description for our HomeAssistant Dashboard.
### Class Definition
To start any AppDaemon app, you need to include a Class that is defined in the `apps.yaml` file. This is also where we
initialize and define the various items that will be used in the remainder of the script.
```python
class birdnet(adbase.ADBase):
def initialize(self):
self.hassapi = self.get_plugin_api("HASS")
self.adapi = self.get_ad_api()
self.mqttapi = self.get_plugin_api("MQTT")
self.birdnet_mqtt = "birdnet"
self.mqttapi.listen_event(
self.birdnet_message, "MQTT_MESSAGE", topic=self.birdnet_mqtt
)
```
For this script, we need to use a lot of the AppDaemon APIs across more than just HomeAssistant, so we're going to be using
`ADBase`. By using that, we can initialize the various APIs, which we do in the next 3 lines. In these 3 lines we need to get
access to HomeAssistant's APIs, AppDaemon's APIs, and MQTT APIs - the first and third items are plugins of AppDaemon, and
AppDaemon is... well... AppDaemon! Here are a few reference docs:
* [MQTT AppDaemon API](https://appdaemon.readthedocs.io/en/latest/MQTT_API_REFERENCE.html)
* [HomeAssistant AppDaemon API](https://appdaemon.readthedocs.io/en/latest/HASS_API_REFERENCE.html)
* [AppDaemon API](https://appdaemon.readthedocs.io/en/latest/AD_API_REFERENCE.html)
These will indispensable to you as you leverage AppDaemon and expand this little script.
Once we have access to that, we need to setup the main topic for MQTT from BirdNET-Pi and finally, what event we are
listening for that will trigger the functions in the rest of the script. `self.birdnet_mqtt = "birdnet"` is the definition
for the MQTT topic. Let's breakdown the last line of the class.
Here's a breakdown of each of the items in that last line. You can find the official documentation [here](https://appdaemon.readthedocs.io/en/latest/MQTT_API_REFERENCE.html#appdaemon.plugins.mqtt.mqttapi.Mqtt.listen_event).
* `self.mqttapi.listen_event` - this is what we use in AppDaemon to listen for an MQTT event in order to trigger a function.
* `self.birdnet_message` - the name of the function you'd like to trigger
* `"MQTT_MESSAGE"` - The default event in AppDaemon's MQTT API plugin. This is used because MQTT doesn't keep a state in this
plugin.
* `topic=self.birdnet_mqtt` - The topic that will be received to trigger the function. Defined on the previous line.
In other words, what we are telling AppDaemon is the following: "When AppDaemon's MQTT API plugin receives a message with the
topic of 'birdnet', run the function `birdnet_message`."
### birdnet_message Function
#### Part 1: Variables Management
Now we get into our first function of the script. The first portion of this script is splitting up the payload that we
defined from the BirdNET-Pi UI into individual variables that we can better manage later on. If you test this script out by
adding `print()` statements at various points, you'll notice that the payload is received with the following json formatting:
```json
{
"payload": {
"data": "data"
}
}
```
As such, we need to look _inside_ the payload to begin grabbing the data. The `pre_split` variable is now just looking at the
data inside the payload and the rest of the variables take all the date into the payload, split it by the comma, and then
grab the string by their index. If you remember what [we did above]({{<ref "birdnet_homeassistant.md#birdnet-pi-notification-setup-mqtt" >}}) above, you'll see that we have the various BirdNET information at each of the indexes in the AppDaemon script - 0 through 5.
#### Part 2: Re-Publishing MQTT Payloads
This next section is shooting all the variables we just defined back via MQTT. The reason why we do it this way is because we
need HomeAssistant to grab each of these variables as individual sensors. BirdNET doesn't give us that capability - it's a
single message with all the information in one. [Here is the documentation from AppDaemon](https://appdaemon.readthedocs.io/en/latest/MQTT_API_REFERENCE.html) on `mqtt_publish`. Later on, I'll show you how to ensure that HomeAssistant takes those topic payloads and adds them as
entities in your HA setup.
#### Part 3: Wikipedia Sensor
The next eight lines are a fairly straightforward [API call to Wikipedia](https://en.wikipedia.org/api/rest_v1/). We start
out by passing the `science_name` into the URL. The rest of the flags that we are passing into the URL comes from Wikipedia's
Docs.
`url = f"https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&exintro&explaintext&redirects=1&titles={science_name}"`
Once that's done we call it with `response.get(url)` and format it with `response.json()`. Wikipedia returns the json payload
with the top level of `query` (which was our action in the url ;) ), and we're looking for the value within that query.
All that's left is to take that query value and push it to HomeAssistant! We can do that with the `self.hassapi.set_state`
function. Within the parenthesis we define the name of the sensor (`sensor.birdnet_wiki`), what it's state should be (`on`),
and any attributes associated with the entity. Since we can't assign a long description to the basic status of the entity,
we're adding an attribute with the key of `description` and the value will be the wikipedia description garnered from the API
call.
```python
url = f"https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&exintro&explaintext&redirects=1&titles={science_name}"
response = requests.get(url)
response = response.json()
for value in response['query']['pages']:
wiki_desc = response['query']['pages'][value]['extract']
self.hassapi.set_state("sensor.birdnet_wiki", state='on',
attributes = {"description": wiki_desc})
```
#### Part 4: Generate Picture for Detection (Optional)
This part is optional but I noticed that BirdNET-Pi was already grabbing a Flickr Picture for it's front end, so I took the
code from the BirdNET code base and adjusted it a bit for my needs. This will work very similarly to the Wikipedia API call,
the main difference here being that you need an API key for Flickr. You can find more [information here](https://www.flickr.com/services/api/misc.api_keys.html).
Given Flickr's fairly robust API, by passing in the detected bird's common name, we get amazing results from the community of
various pictures of the same species of bird. Ever since I've set this up, I've not seen a mislabeled picture in my
dashboard!
The most confusion portion of this section is the `image_url` as you'll notice a bunch of `data["value"]` strings at various
portions of the URL. The short answer to this is in the previous line with the `data` variable. A successful query has Flickr
returning a large payload of information. We're specifically using [this](https://www.flickr.com/services/api/flickr.photos.search.html)
Flickr API endpoint. While you can pass a lot of variables for your needs, if you scroll down, you can see that the example
response contains multiple photos in a single response. We're passing `per_page=5` to limit some of those response items.
Left out of that response, though, is a one-stop-shop for a URL to that photo. Thankfully, Flickr can help us put together a
URL from the data in the response.
{{< box info >}}
_Note: Full Transparency that I only learned about this after reading through BirdNET-Pi's code base. Full credit goes to
[mcguirepr89](https://github.com/mcguirepr89). For additional reference, here is Flickr's [official page on construction
photo image URLS](https://www.flickr.com/services/api/misc.urls.html)_
{{< /box >}}
With this response, we now have the variables we need to construct the URL to actually render the image. Those variables are:
Farm ID, Server ID, ID and Secret. I haven't yet looked into why we need "farm" when the official documentation doesn't state
anything about it.
Almost there! We now do the same as we did with the Wikipedia API response. We create a sensor in HomeAssistant! We're
calling this sensor `sensor.birdpic`, ensuring the `state=on`, and giving it the attributes of the `image_url` as garnered
from Flickr.
```python
headers = {'User-Agent': 'Python_Flickr/1.0'}
flickr_api = "enter_your_api_key"
flickr_url = f"https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key={flickr_api}&text={common_name} bird&sort=relevance&per_page=5&media=photos&format=json&nojsoncallback=1"
flickr_resp = requests.get(url=flickr_url, headers=headers)
data = flickr_resp.json()["photos"]["photo"][0]
image_url = 'https://farm'+str(data["farm"])+'.static.flickr.com/'+str(data["server"])+'/'+str(data["id"])+'_'+str(data["secret"])+'_n.jpg'
self.hassapi.set_state("sensor.birdpic", state='on',
attributes={"image": image_url})
```
## Importing MQTT Sensors into HomeAssistant
Now that we have all the sensors defined and communicating via MQTT, we have one more step to import them into HomeAssistant.
[This MQTT documentation](https://www.home-assistant.io/integrations/mqtt/) by HomeAssistant is good to read about if you
need a broker setup. I will not be going over the broker in this tutorial, but may add one in the future. I tend to like the
yaml configuration for HomeAssistant, so for the sake of this guide, I'll be referencing the [manual configuration of MQTT
items and sensors](https://www.home-assistant.io/integrations/mqtt/#manual-configured-mqtt-items).
To add the sensors from above, open up your `configuration.yaml` file in your favorite editor. You'll then want to add the
mqtt platform and domain:
```yaml
mqtt:
- { domain }:
```
For the BirdNet sensors, we will be using a single domain: `sensor`. Feel free to copy and paste my config from below, but
make sure the names of each entity align with your needs, syntax, and nomenclature/system.
**Full MQTT Sensors in Configuration.yml**
```yaml
mqtt:
sensor:
- name: "Bird Common Name"
state_topic: "birdnet/sensors/common_name"
- name: "Bird Science Name"
state_topic: "birdnet/sensors/science_name"
- name: "Bird Time Seen"
state_topic: "birdnet/sensors/time_seen"
- name: "Bird Date Seen"
state_topic: "birdnet/sensors/date_seen"
- name: "Bird Confidence"
state_topic: "birdnet/sensors/confidence"
value_template: '{{ (value|float(0) *100) | round(1) }}'
unit_of_measurement: '%'
```
You might be looking at the list above and wondering where the Flickr and Wikipedia Description entities are. They were
already created by the AppDaemon script! Specifically, `self.hassapi.set_state()` function will either update the state for
an exisiting entity or, if the entity doesn't exist, it will create a new one.
For the rest of the mqtt payloads, we need HomeAssistant to create them as they come in, which is why we add the above
code block to our HomeAssistant configuration file. To be clear, you _do not_ need to add the Wikipedia and Flickr sensors to
HA's configuration file!
By this point, you should have successfully created 7 new sensors in HomeAssistant. In Part 2 of this article, we'll take a
look at Home Assistant, see what these sensors look like, and create a rudimentary dashboard.
## Birdnet AppDaemon Script
```python
import time
import requests
class birdnet(adbase.ADBase):
def initialize(self):
self.hassapi = self.get_plugin_api("HASS")
self.adapi = self.get_ad_api()
self.mqttapi = self.get_plugin_api("MQTT")
self.birdnet_mqtt = "birdnet"
self.mqttapi.listen_event(
self.birdnet_message, "MQTT_MESSAGE", topic=self.birdnet_mqtt
)
def birdnet_message(self, event_name, data, kwargs):
pre_split = data["payload"]
common_name = pre_split.split(',')[0].strip()
science_name = pre_split.split(',')[1].strip()
date_seen = pre_split.split(',')[2].strip()
time_seen = pre_split.split(',')[3].strip()
week_seen = pre_split.split(',')[4].strip()
confidence = pre_split.split(',')[5].strip()
# print(f"A {common_name} was seen on {date_seen} at {time_seen}. Confidence is {confidence}.")
self.mqttapi.mqtt_publish("birdnet/sensors/common_name", common_name)
self.mqttapi.mqtt_publish("birdnet/sensors/science_name", science_name)
self.mqttapi.mqtt_publish("birdnet/sensors/time_seen", time_seen)
self.mqttapi.mqtt_publish("birdnet/sensors/date_seen", date_seen)
self.mqttapi.mqtt_publish("birdnet/sensors/confidence", confidence)
url = f"https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&exintro&explaintext&redirects=1&titles={science_name}"
response = requests.get(url)
response = response.json()
for value in response['query']['pages']:
wiki_desc = response['query']['pages'][value]['extract']
self.hassapi.set_state("sensor.birdnet_wiki", state='on',
attributes = {"description": wiki_desc})
headers = {'User-Agent': 'Python_Flickr/1.0'}
flickr_api = "enter_your_api_key"
flickr_url = f"https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key={flickr_api}&text={common_name} bird&sort=relevance&per_page=5&media=photos&format=json&nojsoncallback=1"
flickr_resp = requests.get(url=flickr_url, headers=headers)
data = flickr_resp.json()["photos"]["photo"][0]
image_url = 'https://farm'+str(data["farm"])+'.static.flickr.com/'+str(data["server"])+'/'+str(data["id"])+'_'+str(data["secret"])+'_n.jpg'
self.hassapi.set_state("sensor.birdpic", state='on',
attributes={"image": image_url})
```
<style>
.box-shortcode {
color: #e8e8e8;
border: none;
}
.post-content img {
margin: auto
}
</style>