diff --git a/README.md b/README.md
index 4e2e4af..46998ef 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,326 @@
-# sense-collector
\ No newline at end of file
+## About The Project
+
+
+
+**Sense Collector** is a set of scripts deployed with Docker that provide a way of collecting data from the [Sense](https://sense.com/) energy monitoring system. Once deployed, this collection of Grafana dashboards will help you start visualizing that data. If you're just getting started with Grafana, InfluxDB, and Sense - you may want to check out my [Sense Dashboards AIO](https://github.com/lux4rd0/sense-dashboards-aio) (All In One) project.
+
+A live set of dashboards using this Collector [are available](https://labs.lux4rd0.com/sense-collector/) for you to try out.
+
+## Getting Started
+
+The project builds a pre-configured Docker container that takes different configurations based on how often you wish to poll your Sense device and where you want to store your data.
+
+## Prerequisites
+
+- [Docker](https://docs.docker.com/install)
+- [Docker Compose](https://docs.docker.com/compose/install)
+- [InfluxDB 1.8](https://docs.influxdata.com/influxdb/v1.8/) or [Grafana Loki 2.2](https://grafana.com/oss/loki/)
+- [Grafana 8.0.6](https://grafana.com/oss/grafana/)
+
+## Deploying the Sense Collector
+
+Use the following [Docker container](https://hub.docker.com/r/lux4rd0/sense-collector):
+
+ lux4rd0/sense-collector:1.0.1
+ lux4rd0/sense-collector:latest
+
+Correct environmental variables are required for the container to function. It mainly includes a **Monitor ID** and a corresponding **authentication token**. If you don't have those, the following script may be used:
+
+ generate_docker-compose.sh
+
+The script takes the following details about your InfluxDB and your Sense credentials as environmental variables:
+
+ SENSE_COLLECTOR_INFLUXDB_PASSWORD
+ SENSE_COLLECTOR_INFLUXDB_URL
+ SENSE_COLLECTOR_INFLUXDB_USERNAME
+ SENSE_COLLECTOR_PASSWORD
+ SENSE_COLLECTOR_USERNAME
+
+The username and password are the same that you use in your Sense mobile app or the Sense Web app.
+
+An example command line would be (be sure to use your own personal access token):
+
+ SENSE_COLLECTOR_INFLUXDB_PASSWORD="5Q7c7hwQtsZtCrXW" \
+ SENSE_COLLECTOR_INFLUXDB_URL="http://influxdb01.com:8086/write?db=sense" \
+ SENSE_COLLECTOR_INFLUXDB_USERNAME="senseuser" \
+ SENSE_COLLECTOR_PASSWORD="twFQ8P3XBj55DvMw" \
+ SENSE_COLLECTOR_USERNAME="lux4rd0@domain.com" \
+ bash ./generate_docker-compose.sh
+
+The following file will be generated for you:
+
+#### `docker-compose.yml`
+
+An example of this docker-compose.yml file is included in this repository.
+
+ services:
+ sense-collector:
+ container_name: sense-collector-72535
+ environment:
+ SENSE_COLLECTOR_HOST_HOSTNAME: sense-collector.lux4rd0.com
+ SENSE_COLLECTOR_INFLUXDB_PASSWORD: none
+ SENSE_COLLECTOR_INFLUXDB_URL: http://influxdb01.lux4rd0.com:8086/write?db=sense
+ SENSE_COLLECTOR_INFLUXDB_USERNAME: none
+ SENSE_COLLECTOR_MONITOR_ID: 72535
+ SENSE_COLLECTOR_TOKEN: t1.1476.1474.8e6dc77daf22e1fb471d7b942w97e477d1es53bcf2d72
+ TZ: America/Chicago
+ image: lux4rd0/sense-collector:latest
+ restart: always
+ version: '3.3'
+
+If you don't want to use docker-compose, an example docker run command will be displayed on the screen.
+
+ docker run --rm \
+ --name=sense-collector-72535 \
+ -e SENSE_COLLECTOR_HOST_HOSTNAME=app02.tylephony.com \
+ -e SENSE_COLLECTOR_INFLUXDB_PASSWORD=5Q7c7hwQtsZtCrXW \
+ -e SENSE_COLLECTOR_INFLUXDB_URL=http://influxdb01.lux4rd0.com:8086/write?db=sense \
+ -e SENSE_COLLECTOR_INFLUXDB_USERNAME=senseuser \
+ -e SENSE_COLLECTOR_MONITOR_ID=72535 \
+ -e SENSE_COLLECTOR_TOKEN=t1.1476.1474.8e6dc77daf22e1fb471d7b942w97e477d1es53bcf2d72 \
+ -e TZ=America/Chicago \
+ --restart always \
+ lux4rd0/sense-collector:latest
+
+Running `docker-compose up -d' or the `docker-run` command will download and start up the sense-collector container.
+
+## Environmental Flags:
+
+The Docker contain can be configured with additional environment flags to control collector behaviors. They are descript below:
+
+`SENSE_COLLECTOR_DEBUG` - OPTIONAL
+
+Outputs additional logging. Defaults to false.
+
+- false
+- true
+
+`SENSE_COLLECTOR_DEBUG_CURL` - OPTIONAL
+
+Outputs additional logging specific to the curl commands to collect data from Sense and persist data to InfluxDB. Defaults to false.
+
+- true
+- false
+
+`SENSE_COLLECTOR_DEBUG_IF` - OPTIONAL
+
+Outputs additional logging specific to script validation. Defaults to false.
+
+- true
+- false
+
+`SENSE_COLLECTOR_DEBUG_SLEEPING` - OPTIONAL
+
+Outputs additional logging specific to when the Collector is sleeping between polling. Sometimes you want to see if it's doing anything. Defaults to false.
+
+- true
+- false
+
+`SENSE_COLLECTOR_DISABLE_DEVICE_DETAILS` - OPTIONAL
+
+Disables or enables the Device Details process. Defaults to false if not set.
+
+- true
+- false
+
+`SENSE_COLLECTOR_DISABLE_HEALTH_CHECK` - OPTIONAL
+
+Disables or enables the Health Check process. Defaults to false if not set.
+
+- true
+- false
+
+`SENSE_COLLECTOR_DISABLE_HOST_PERFORMANCE` - OPTIONAL
+
+Disables or enables the Host Performance process. Defaults to false if not set.
+
+- true
+- false
+
+`SENSE_COLLECTOR_DISABLE_MONITOR_STATUS` - OPTIONAL
+
+Disables or enables the Monitor Status process. Defaults to false if not set.
+
+- true
+- false
+
+`SENSE_COLLECTOR_DISABLE_SENSE_COLLECTOR` - OPTIONAL
+
+Disables or enables the Sense Collector process. Defaults to false if not set.
+
+- true
+- false
+
+`SENSE_COLLECTOR_HOST_HOSTNAME` - OPTIONAL
+
+This value represents the hostname that is running the Docker container. Docker creates a unique hostname each time a docker container is recycled. This entry is used in the Collector Info dashboard to know where the Collector is running. This value is populated when the `generate_docker-compose.sh` script generates the `docker-compose.yml` file.
+
+`SENSE_COLLECTOR_HOST_PERFORMANCE_POLL_INTERVAL` - OPTIONAL
+
+Time in seconds that the Host Performance process polls the host for performance details. Defaults to 60 (seconds).
+
+`SENSE_COLLECTOR_INFLUXDB_PASSWORD` - REQUIRED
+
+The password to your InfluxDB database instance. If you use the `generate_docker-compose.sh` script, it defaults to "password". It is a **required** environment variable.
+
+`SENSE_COLLECTOR_INFLUXDB_URL` - REQUIRED
+
+The URL required to persist data to InfluxDB. If you use the `generate_docker-compose.sh` script, it defaults to "http://influxdb:8086/write?db=sense". It is a **required** environment variable.
+
+`SENSE_COLLECTOR_INFLUXDB_USERNAME` - REQUIRED
+
+The username to your InfluxDB database instance. If you use the `generate_docker-compose.sh` script, it defaults to "influxdb". It is a **required** environment variable.
+
+`SENSE_COLLECTOR_MONITOR_ID` - REQUIRED
+
+The ID of your Sense monitor. If you use the generate_docker-compose.sh script, it will be automatically added to your docker-compose.yml or docker-compose run command for you based on your Sense username and password. There's no way to know this ID from the Sense mobile or Web app. It is a **required** environment variable.
+
+`SENSE_COLLECTOR_MONITOR_STATUS_POLL_INTERVAL` - OPTIONAL
+
+Time in seconds that the Monitor Status process polls the Sense monitor for details. Defaults to 60 (seconds).
+
+`SENSE_COLLECTOR_SENSE_COLLECTOR_POLL_INTERVAL` - OPTIONAL
+
+Determines how often the core Sense Collector polls mains and device information. There are two types of collections possible:
+
+ - stream
+
+Allows for ingesting all of the stream data from the Sense socket API. The data is generally updated about two times a second and provides a very high granularity of metric data on all of your mains and devices. However, depending on the performance of the running Sense Collector host, it may fall behind on processing data as well as possibly higher CPU utilization. Take a look at the Collector Info dashboard for more understanding of the Sense Collector utilization.
+
+or
+
+ - 5, 10, 15, 30, or 60
+
+If streaming data is not an option or a desire to reduce the amount of CPU being consumed, use one of these settings (measured in seconds). When these settings are provided, the data stream is still passed through the Sense Collector to listen for timeline events but will only process mains and device details on the polling interval provided.
+
+`SENSE_COLLECTOR_THREADS` - OPTIONAL
+
+The number of threads used for processing device details. Defaults to 4. Threads should be set to something close to the number of CPUs on your host. For slower processing hosts, lowering this and using a polling interval may be helpful.
+
+`SENSE_COLLECTOR_TOKEN` - REQUIRED
+
+The authentication token for your Sense monitor. If you use the generate_docker-compose.sh script, it will be automatically added to your docker-compose.yml or docker-compose run command for you based on your Sense username and password. There's no way to obtain this token from the Sense mobile or Web app. It is a **required** environment variable.
+
+## Collector Details
+
+#### sense-collector
+
+Sense Collector is the primary data collector and is responsible for gathering details on the following:
+
+Voltage, Watts, Hz on the mains, and device-specific wattage details. If you happen to have any [Sense compatible](https://help.sense.com/hc/en-us/articles/360012089393-What-smart-plugs-are-compatible-with-Sense-) smart plugs, their additional metric details of voltage and amps are collected.
+
+Timeline events for devices change of states (on, off, idle)
+
+#### device-details
+
+Device Details polls the Sense API to gather details on each of your devices. This includes:
+
+avg_duration, avg_monthly_KWH, avg_monthly_cost, avg_monthly_cost, avg_monthly_pct, avg_monthly_runs, avg_watts, current_ao_wattage, current_month_KWH, current_month_cost, current_month_runs, icon, last_state, last_state_time, name, yearly_KWH, yearly_cost, yearly_text
+
+#### host-performance
+
+Host Performance is a process that gathers CPU, process details, netstat, process counts, and memory utilization. These details are viewable in the Collector Info Grafana dashboard.
+
+#### monitor-status
+
+Monitor Status gathers details about the Sense monitor and detection status for both in progress and found devices. Monitor details include: emac, ethernet, ip_address, mac, ndt_enabled, online, progress, serial, signal, ssid, status, test_result, version, wifi_strength
+
+#### health-check
+
+Health Check is a function that runs every 60 seconds to validate the health of the running processes. If no data has been collected or persisted to InfluxDB and this parameter is set to true, the docker container will be marked as Unhealthy and terminate. Setting this to false will always return a healthy response to the Docker health check. The health check is included as there may be times when the socket connection goes silent, and recycling the container is the only way to get it listening again.
+
+## Grafana Dashboards
+
+Collecting data is only half the fun. Now it's time to provision some Grafana Dashboards to visualize all of your essential Sense data. You'll find a [folder of dashboards](https://github.com/lux4rd0/sense-collector/dashboards) with collectors and backends split out. You can also use the links/numbers next to each dashboard title to load the dashboards in [directly from Grafana](https://grafana.com/grafana/dashboards?search=sense%20collector).
+
+### In General:
+
+Each dashboard has dropdowns at the top that provide for filtering of measurements based on devices and plugs. They default to "All," but you can certainly select and save preferences.
+
+**Interval**: A dropdown that provides some different levels of smoothing helps manage how the graphs look based on the interval of data being collected by the Sense Collector. Think of this as a level of "smoothing" based on the time frame you've chosen and the quantity of data collected (streaming versus polled data.)
+
+**Device Status On/Off/Idle**: These three toggles provide for the overlay of event annotations in the Wattage By Devices panels as well as the Wattage, Volts, and Hz panels in the Mains Overview dashboard.
+
+**Sense Collector Dashboard Links**: This dropdown in the top right-hand corner provides easy access to the other dashboards in this collection. The links maintain the current time range and any variables that have been changed between dashboards. Meaning - if you change the time range from "Today so far" to "Last 7 days" it'll stay the same between dashboards. Same for any selected devices and Interval smoothing.
+
+**Time Range**: This defaults to "Today so far" but can be updated to any other Relative or Absolute time range. For longer time ranges, be sure to make changes to the "Interval" dropdown if you want to smooth out any of the data.
+
+**Dashboard Refresh**: Each of the dashboards are set to refresh every sixty seconds. The refresh can be changed or disabled altogether.
+
+### Collector Info - [14734](https://grafana.com/grafana/dashboards/14734)
+
+
+
+**Collector Info**: Provides observability into how the Sense Collector functions alongside metrics related to the host's performance. This dashboard helps understand the performance of the main collector functions to assist with troubleshooting.
+
+**Epoch Time Difference**: Helps determine if your hosts can keep up with processing messages from the Sense monitor. It provides the difference between the host time and the epoch time received in the Sense monitor data. Negative numbers mean the Sense monitor is ahead of the hosts. Positive numbers mean the host is behind the Sense monitor. If the drift trends to the positive, it may also mean that there's just time clock drift. Ensure you keep an eye on the NTP time sync on both your host and Sense if there's a large discrepancy.
+
+**CPU, Load Average, Memory Utilization**: These panels show host-level details and are not specific to the performance of the docker container. Per Process CPU Usage, Netstat, and Processes are particular to the container.
+
+**Collector Starts**: Provide the last time the container and process were started. The connection is reset every ten minutes due to the way Sense times out the connection.
+
+### Device Overview - [14735](https://grafana.com/grafana/dashboards/14735)
+
+
+
+**Device Overview** is the main dashboard for Sense Collector. Here you'll see several different sections about both overall and detailed device details.
+
+**Current Wattage**: A Bubble Chart showing current wattage usage by device. Larger circles represent higher wattage consumption.
+
+**Wattage By Device (Stacked)**: Wattage overtime per device. The graph is stacked to represent total household wattage.
+
+**Device Status**: This is a State Timeline representing event data from the Sensor Monitor over time. It currently represents three states, On, Off, and Idle.
+
+**Device Details - Average**: A table view of current data representing the current state, state time, watts in use, average, and always on makeup. Average duration, monthly kWh, percent, and the number of runs are also listed. This table defaults to be sorted by "Watts (In Use)."
+
+**Device Details - Current Month**: Shows the number of runs per device since the start of the current month.
+
+**Device Details - Yearly**: Shows calculated kWh and costs for each device. The text next to each device provides details on how the costs are calculated. For example, "Based on the last 30 days," "Based on the last 7 days," or "Based on last season."
+
+**Plug Details**: If you have any of the [Sense compatible](https://help.sense.com/hc/en-us/articles/360012089393-What-smart-plugs-are-compatible-with-Sense-) smart plugs, they will be listed here as well. Open up this row to display Volts and Amps measured by each plug. There's another measurement (Who Knows By Plugs), but I don't know what it does yet.
+
+**Always On Devices**: Shows which devices that the Sense monitor has detected to have an Always On wattage component. This may be different than actual wattage and tends to update less frequently.
+
+> **Notice**: This Grafana dashboard uses the community visualization [Bubble Chart](https://grafana.com/grafana/plugins/digrich-bubblechart-panel/) panel plugin. It hasn't been updated for quite some time and won't work out of the box with current versions of Grafana due to plugin signing requirements. [Configuration changes](https://grafana.com/docs/grafana/latest/administration/configuration/#allow_loading_unsigned_plugins) on your Grafana instance will be needed to load this plugin.
+
+### Mains Overview - [14736](https://grafana.com/grafana/dashboards/14736)
+
+
+
+**Mains Overview** provides three panels showing Wattage (Stacked), Voltages, and Frequency. There are dropdowns at the top of the dashboard to show Leg 1, Leg 2, or both together. Device Status On, Off, and Idle event annotations may be toggled on or off.
+
+### Monitor & Detection - [14737](https://grafana.com/grafana/dashboards/14737)
+
+
+
+The **Monitor & Detection** dashboard provides observability of the monitor itself.
+
+**Device Detection Status**: Represents Device Detection Status for both "Found" and "In Progress" devices.
+
+**Wifi Signal Strength - RSSI**: Represents the Wifi signal strength of your Sense monitor.
+
+**Monitor Details**: This panel shows current information about Online status, General Status, Learning Progress, IP Address, MAC Address, Wifi SSID, Wifi Strength, Ethernet, NDT Enabled, and software version.
+
+## Multiple Devices
+
+The Sense Collector and dashboards currently only support a single Sense instance. If you have more than one Sense device and are interested in having multiple devices supported, please let me know.
+
+### Time Zone Variable
+
+A TZ variable is used when running the Docker container based on what value is returned during the initial docker-compose configuration scripts. It's not currently in use in any of the current dashboards.
+
+## Roadmap
+
+See the open issues for a list of proposed features (and known issues).
+
+## Contact
+
+Dave Schmid: [@lux4rd0](https://twitter.com/lux4rd0) - dave@pulpfree.org
+
+Project Link: https://github.com/lux4rd0/sense-collector
+
+## Acknowledgements
+
+- Grafana Labs - [https://grafana.com/](https://grafana.com/)
+- Grafana - [https://grafana.com/oss/grafana/](https://grafana.com/oss/grafana/)
+- Grafana Dashboard Community - [https://grafana.com/grafana/dashboards/](https://grafana.com/grafana/dashboards/)
diff --git a/docker-compose.yml b/docker-compose.yml
index 81c678d..e44a99a 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,16 +1,13 @@
services:
sense-collector:
- container_name: sense-collector
+ container_name: sense-collector-72535
environment:
- SENSE_COLLECTOR_DEBUG: "false"
- SENSE_COLLECTOR_DEBUG_CURL: "false"
- SENSE_COLLECTOR_HEALTHCHECK: "true"
- SENSE_COLLECTOR_HOST_HOSTNAME: app02.tylephony.com
+ SENSE_COLLECTOR_HOST_HOSTNAME: sense-collector.lux4rd0.com
SENSE_COLLECTOR_INFLUXDB_PASSWORD: none
- SENSE_COLLECTOR_INFLUXDB_URL: http://influxdb01.tylephony.com:8086/write?db=sense
+ SENSE_COLLECTOR_INFLUXDB_URL: http://influxdb01.lux4rd0.com:8086/write?db=sense
SENSE_COLLECTOR_INFLUXDB_USERNAME: none
- SENSE_COLLECTOR_MONITOR_ID: <>
- SENSE_COLLECTOR_TOKEN: <>
+ SENSE_COLLECTOR_MONITOR_ID: 72535
+ SENSE_COLLECTOR_TOKEN: t1.1476.1474.8e6dc77daf22e1fb471d7b942w97e477d1es53bcf2d72
TZ: America/Chicago
image: lux4rd0/sense-collector:latest
restart: always
diff --git a/generate_docker-compose.sh b/generate_docker-compose.sh
index a0012ad..98f1981 100644
--- a/generate_docker-compose.sh
+++ b/generate_docker-compose.sh
@@ -4,22 +4,23 @@
## Sense Collector - generate_docker-compose.sh
##
-debug=$SENSE_COLLECTOR_DEBUG
-debug_curl=$SENSE_COLLECTOR_DEBUG_CURL
+##
+## Set Specific Variables
+##
+
+collector_type="sense-collector"
+
influxdb_password=$SENSE_COLLECTOR_INFLUXDB_PASSWORD
influxdb_url=$SENSE_COLLECTOR_INFLUXDB_URL
influxdb_username=$SENSE_COLLECTOR_INFLUXDB_USERNAME
-loki_client_url=$SENSE_COLLECTOR_LOKI_CLIENT_URL
sense_password=$SENSE_COLLECTOR_PASSWORD
sense_username=$SENSE_COLLECTOR_USERNAME
-
echo_bold=$(tput -T xterm bold)
echo_color_sense=$(echo -e "\e[3$(( RANDOM * 6 / 32767 + 1 ))m")
echo_color_collector=$(echo -e "\e[3$(( RANDOM * 6 / 32767 + 1 ))m")
echo_normal=$(tput -T xterm sgr0)
-
echo "
███████╗███████╗███╗ ██╗███████╗███████╗
@@ -38,34 +39,30 @@ echo "
"
-
-
echo "${echo_bold}Sense Collector${echo_normal} (generate_docker-compose.sh) - https://github.com/lux4rd0/sense-collector
-debug=${debug}
-debug_curl=${debug_curl}
-influxdb_password=${influxdb_password}
-influxdb_url=${influxdb_url}
-influxdb_username=${influxdb_username}
-loki_client_url=${loki_client_url}
-sense_password=${sense_password}
-sense_username=${sense_username}"
+${echo_bold}influxdb_password${echo_normal}=${influxdb_password}
+${echo_bold}influxdb_url${echo_normal}=${influxdb_url}
+${echo_bold}influxdb_username${echo_normal}=${influxdb_username}
+${echo_bold}sense_password${echo_normal}=${sense_password}
+${echo_bold}sense_username${echo_normal}=${sense_username}
+"
-if [ -z "${influxdb_password}" ]; then echo "${echo_bold}${echo_color_sense}sense-collector:${echo_normal} ${echo_bold}SENSE_COLLECTOR_INFLUXDB_PASSWORD${echo_normal} was not set. Setting defaults: ${echo_bold}password${echo_normal}"; influxdb_password="password"; fi
+if [ -z "${influxdb_password}" ]; then echo "${echo_bold}${echo_color_sense}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_INFLUXDB_PASSWORD${echo_normal} was not set. Setting defaults: ${echo_bold}password${echo_normal}"; influxdb_password="password"; fi
-if [ -z "${influxdb_url}" ]; then echo "${echo_bold}${echo_color_sense}sense-collector:${echo_normal} ${echo_bold}SENSE_COLLECTOR_INFLUXDB_URL${echo_normal} was not set. Setting defaults: ${echo_bold}http://influxdb:8086/write?db=sense${echo_normal}"; influxdb_url="http://influxdb:8086/write?db=sense" ; fi
+if [ -z "${influxdb_url}" ]; then echo "${echo_bold}${echo_color_sense}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_INFLUXDB_URL${echo_normal} was not set. Setting defaults: ${echo_bold}http://influxdb:8086/write?db=sense${echo_normal}"; influxdb_url="http://influxdb:8086/write?db=sense" ; fi
-if [ -z "${influxdb_username}" ]; then echo "${echo_bold}${echo_color_sense}sense-collector:${echo_normal} ${echo_bold}SENSE_COLLECTOR_INFLUXDB_USERNAME${echo_normal} was not set. Setting defaults: ${echo_bold}influxdb${echo_normal}"; influxdb_username="influxdb"; fi
+if [ -z "${influxdb_username}" ]; then echo "${echo_bold}${echo_color_sense}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_INFLUXDB_USERNAME${echo_normal} was not set. Setting defaults: ${echo_bold}influxdb${echo_normal}"; influxdb_username="influxdb"; fi
-if [ -z "${sense_password}" ]; then echo "${echo_bold}${echo_color_sense}sense-collector:${echo_normal} ${echo_bold}SENSE_COLLECTOR_PASSWORD${echo_normal} was not set. Missing Sense password. Please provide your password as an environmental variable."; fi
+if [ -z "${sense_password}" ]; then echo "${echo_bold}${echo_color_sense}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_PASSWORD${echo_normal} was not set. Missing Sense password. Please provide your password as an environmental variable."; fi
-if [ -z "${sense_username}" ]; then echo "${echo_bold}${echo_color_sense}sense-collector:${echo_normal} ${echo_bold}SENSE_COLLECTOR_USERNAME${echo_normal} was not set. Missing Sense user name. Please provide your user name as an environmental variable."
+if [ -z "${sense_username}" ]; then echo "${echo_bold}${echo_color_sense}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_USERNAME${echo_normal} was not set. Missing Sense user name. Please provide your user name as an environmental variable."
else
url_sense_authenticate="https://api.sense.com/apiservice/api/v1/authenticate"
-response_url_sense=$(curl -k --data "email=${sense_username}" --data "password=${sense_password}" -H "Sense-Collector-Client-Version: 1.0.0" -H "X-Sense-Protocol: 3" -H "User-Agent: okhttp/3.8.0" "${url_sense_authenticate}")
+response_url_sense=$(curl --silent --show-error --fail -k --data "email=${sense_username}" --data "password=${sense_password}" -H "Sense-Collector-Client-Version: 1.0.0" -H "X-Sense-Protocol: 3" -H "User-Agent: okhttp/3.8.0" "${url_sense_authenticate}")
#echo "${response_url_sense}"
@@ -73,21 +70,20 @@ token=$(echo "${response_url_sense}" | jq -r .access_token)
monitor_id=$(echo "${response_url_sense}" | jq -r .monitors[].id)
time_zone=$(echo "${response_url_sense}" | jq -r .monitors[].time_zone)
-echo "token=${token}
-monitor_id=${monitor_id}
-time_zone=${time_zone}"
-
+echo "${echo_bold}token${echo_normal}=${token}
+${echo_bold}monitor_id${echo_normal}=${monitor_id}
+${echo_bold}time_zone${echo_normal}=${time_zone}
+"
FILE_DC="${PWD}/docker-compose.yml"
if test -f "${FILE_DC}"; then
existing_file_timestamp_dc=$(date -r "${FILE_DC}" "+%Y%m%d-%H%M%S")
-echo "${echo_bold}${echo_color_sense}sense-collector:${echo_normal} Existing ${echo_bold}${FILE_DC}${echo_normal} with a timestamp of ${echo_bold}${existing_file_timestamp_dc}${echo_normal} file found. Backup up file to ${FILE_DC}.${existing_file_timestamp_dc}.old"
+echo "${echo_bold}${echo_color_sense}sense-collector:${echo_normal} Existing ${echo_bold}${FILE_DC}${echo_normal} with a timestamp of ${echo_bold}${existing_file_timestamp_dc}${echo_normal} file found. Backup up file to ${echo_bold}${FILE_DC}.${existing_file_timestamp_dc}.old${echo_normal}
+"
mv "${FILE_DC}" "${FILE_DC}"."${existing_file_timestamp_dc}.old"
fi
-
-
##
## ┌┬┐┌─┐┌─┐┬┌─┌─┐┬─┐ ┌─┐┌─┐┌┬┐┌─┐┌─┐┌─┐┌─┐┬ ┬┌┬┐┬
## │││ ││ ├┴┐├┤ ├┬┘───│ │ ││││├─┘│ │└─┐├┤ └┬┘││││
@@ -99,9 +95,6 @@ echo "services:
container_name: sense-collector-${monitor_id}
environment:
TZ: ${time_zone}
- SENSE_COLLECTOR_DEBUG: \"false\"
- SENSE_COLLECTOR_DEBUG_CURL: \"false\"
- SENSE_COLLECTOR_HEALTHCHECK: \"true\"
SENSE_COLLECTOR_HOST_HOSTNAME: $(hostname)
SENSE_COLLECTOR_INFLUXDB_PASSWORD: ${influxdb_password}
SENSE_COLLECTOR_INFLUXDB_URL: ${influxdb_url}
@@ -121,6 +114,25 @@ echo " SENSE_COLLECTOR_TOKEN: ${token}
restart: always
version: '3.3'" >> docker-compose.yml
-echo "${echo_bold}${echo_color_sense}sense-collector:${echo_normal} ${FILE_DC} file created"
+echo "${echo_bold}${echo_color_sense}sense-collector:${echo_normal} ${echo_bold}${FILE_DC}${echo_normal} file created"
+
+fi
+
+echo "
+You may also use this docker run command:
+"
+
+echo "docker run --rm \\
+ --name=sense-collector-${monitor_id} \\
+ -e SENSE_COLLECTOR_HOST_HOSTNAME=$(hostname) \\
+ -e SENSE_COLLECTOR_INFLUXDB_PASSWORD=${influxdb_password} \\
+ -e SENSE_COLLECTOR_INFLUXDB_URL=${influxdb_url} \\
+ -e SENSE_COLLECTOR_INFLUXDB_USERNAME=${influxdb_username} \\
+ -e SENSE_COLLECTOR_MONITOR_ID=${monitor_id} \\
+ -e SENSE_COLLECTOR_TOKEN=${token} \\
+ -e TZ=America/Chicago \\
+ --restart always \\
+ lux4rd0/sense-collector:latest"
+
+
-fi
\ No newline at end of file
diff --git a/grafana/shared/readme.md b/grafana/shared/readme.md
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/grafana/shared/readme.md
@@ -0,0 +1 @@
+
diff --git a/grafana/shared/sense_collector-collector_info.json b/grafana/shared/sense_collector-collector_info.json
new file mode 100644
index 0000000..aa045db
--- /dev/null
+++ b/grafana/shared/sense_collector-collector_info.json
@@ -0,0 +1,1991 @@
+{
+ "__inputs": [
+ {
+ "name": "DS_INFLUXDB_- SENSE",
+ "label": "InfluxDB - sense",
+ "description": "",
+ "type": "datasource",
+ "pluginId": "influxdb",
+ "pluginName": "InfluxDB"
+ }
+ ],
+ "__requires": [
+ {
+ "type": "grafana",
+ "id": "grafana",
+ "name": "Grafana",
+ "version": "8.0.6"
+ },
+ {
+ "type": "datasource",
+ "id": "influxdb",
+ "name": "InfluxDB",
+ "version": "1.0.0"
+ },
+ {
+ "type": "panel",
+ "id": "stat",
+ "name": "Stat",
+ "version": ""
+ },
+ {
+ "type": "panel",
+ "id": "table",
+ "name": "Table",
+ "version": ""
+ },
+ {
+ "type": "panel",
+ "id": "timeseries",
+ "name": "Time series",
+ "version": ""
+ }
+ ],
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "target": {
+ "limit": 100,
+ "matchAny": false,
+ "tags": [],
+ "type": "dashboard"
+ },
+ "type": "dashboard"
+ }
+ ]
+ },
+ "description": "",
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "id": null,
+ "iteration": 1626665920803,
+ "links": [
+ {
+ "asDropdown": true,
+ "icon": "external link",
+ "includeVars": true,
+ "keepTime": true,
+ "tags": [
+ "sense-collector"
+ ],
+ "targetBlank": false,
+ "title": "Sense Collector - Dashboards",
+ "tooltip": "",
+ "type": "dashboards",
+ "url": ""
+ }
+ ],
+ "panels": [
+ {
+ "collapsed": false,
+ "datasource": null,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 30,
+ "panels": [],
+ "title": "Collector Duration Stats",
+ "type": "row"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "ns"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 11,
+ "w": 12,
+ "x": 0,
+ "y": 1
+ },
+ "id": 103,
+ "interval": "$interval",
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "table",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "targets": [
+ {
+ "alias": "$tag_source - $tag_function",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "source"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "function"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "0"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "sense_o11y",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "duration"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "source",
+ "operator": "=~",
+ "value": "/^$source$/"
+ },
+ {
+ "condition": "AND",
+ "key": "function",
+ "operator": "=~",
+ "value": "/^$function$/"
+ }
+ ]
+ }
+ ],
+ "title": "Function Timer",
+ "type": "timeseries"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 11,
+ "w": 12,
+ "x": 12,
+ "y": 1
+ },
+ "id": 105,
+ "interval": "$interval",
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "single"
+ }
+ },
+ "targets": [
+ {
+ "alias": "$tag_source - $tag_function",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "function"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "source"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "previous"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "sense_o11y",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "difference_epoch"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "source",
+ "operator": "=~",
+ "value": "/^$source$/"
+ },
+ {
+ "condition": "AND",
+ "key": "function",
+ "operator": "=~",
+ "value": "/^$function$/"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "title": "Epoch Time Difference",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "datasource": null,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 12
+ },
+ "id": 24,
+ "panels": [],
+ "repeat": "collector_key",
+ "title": "Host Observability - $host_hostname",
+ "type": "row"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "axisSoftMax": 0,
+ "axisSoftMin": 0,
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "graph": false,
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "percent"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 0,
+ "y": 13
+ },
+ "id": 43,
+ "interval": "$interval",
+ "options": {
+ "graph": {},
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "pluginVersion": "7.5.5",
+ "targets": [
+ {
+ "alias": "$col",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "host_hostname"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "previous"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "sense_o11y",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT mean(\"cpu_idle\") AS \"Idle\", mean(\"cpu_iowait\"), mean(\"cpu_sys\"), mean(\"cpu_usr\") FROM \"sense_o11y\" WHERE (\"host_hostname\" = 'docker02.tylephony.com') AND $timeFilter GROUP BY time($__interval), \"host_hostname\" fill(previous)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "cpu_iowait"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "IO Wait"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "cpu_sys"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "System"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "cpu_usr"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "User"
+ ],
+ "type": "alias"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "host_hostname",
+ "operator": "=~",
+ "value": "/^$host_hostname$/"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "CPU",
+ "type": "timeseries"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "graph": false,
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 8,
+ "y": 13
+ },
+ "id": 52,
+ "interval": "$interval",
+ "options": {
+ "graph": {},
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "pluginVersion": "7.5.5",
+ "targets": [
+ {
+ "alias": "$col minute",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "host_hostname"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "previous"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "sense_o11y",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT mean(\"cpu_idle\") AS \"Idle\", mean(\"cpu_iowait\"), mean(\"cpu_sys\"), mean(\"cpu_usr\") FROM \"sense_o11y\" WHERE (\"host_hostname\" = 'docker02.tylephony.com') AND $timeFilter GROUP BY time($__interval), \"host_hostname\" fill(previous)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "loadavg_one"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "1"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "loadavg_five"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "5"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "loadavg_fifteen"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "15"
+ ],
+ "type": "alias"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "host_hostname",
+ "operator": "=~",
+ "value": "/^$host_hostname$/"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Load Average",
+ "type": "timeseries"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "graph": false,
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 16,
+ "y": 13
+ },
+ "id": 71,
+ "interval": "$interval",
+ "options": {
+ "graph": {},
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "right"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "pluginVersion": "7.5.3",
+ "targets": [
+ {
+ "alias": "$tag_sysperf_command",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "sysperf_command"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "previous"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "sense_o11y",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "total_time"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [],
+ "type": "non_negative_difference"
+ },
+ {
+ "params": [
+ " / 100"
+ ],
+ "type": "math"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "sysperf_type",
+ "operator": "=",
+ "value": "process_utilization"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Per Process CPU Usage (Seconds)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "graph": false,
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 0,
+ "y": 19
+ },
+ "id": 101,
+ "interval": "$interval",
+ "options": {
+ "graph": {},
+ "legend": {
+ "calcs": [],
+ "displayMode": "table",
+ "placement": "right"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "pluginVersion": "7.5.5",
+ "targets": [
+ {
+ "alias": "$tag_netstat_app - $col",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "host_hostname"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "netstat_app"
+ ],
+ "type": "tag"
+ }
+ ],
+ "measurement": "sense_o11y",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT mean(\"cpu_idle\") AS \"Idle\", mean(\"cpu_iowait\"), mean(\"cpu_sys\"), mean(\"cpu_usr\") FROM \"sense_o11y\" WHERE (\"host_hostname\" = 'docker02.tylephony.com') AND $timeFilter GROUP BY time($__interval), \"host_hostname\" fill(previous)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "netstat_closewait"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "Close Wait"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "netstat_established"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "Established"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "netstat_finwait2"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "Finwait2"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "netstat_listen"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "Listen"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "netstat_timewait"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "Timewait"
+ ],
+ "type": "alias"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "host_hostname",
+ "operator": "=~",
+ "value": "/^$host_hostname$/"
+ },
+ {
+ "condition": "AND",
+ "key": "sysperf_type",
+ "operator": "=",
+ "value": "netstat"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Netstat (Container)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "graph": false,
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 8,
+ "y": 19
+ },
+ "id": 69,
+ "interval": "$interval",
+ "options": {
+ "graph": {},
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "pluginVersion": "7.5.5",
+ "targets": [
+ {
+ "alias": "$col",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "host_hostname"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "previous"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "sense_o11y",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT mean(\"cpu_idle\") AS \"Idle\", mean(\"cpu_iowait\"), mean(\"cpu_sys\"), mean(\"cpu_usr\") FROM \"sense_o11y\" WHERE (\"host_hostname\" = 'docker02.tylephony.com') AND $timeFilter GROUP BY time($__interval), \"host_hostname\" fill(previous)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "processes_running"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "Running"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "processes_sleeping"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "Sleeping"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "processes_stopped"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "Stopped"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "processes_zombie"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "Zombie"
+ ],
+ "type": "alias"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "host_hostname",
+ "operator": "=~",
+ "value": "/^$host_hostname$/"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Processes (Container)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "graph": false,
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineStyle": {
+ "fill": "solid"
+ },
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "deckbytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 16,
+ "y": 19
+ },
+ "id": 53,
+ "interval": "$interval",
+ "options": {
+ "graph": {},
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "right"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "pluginVersion": "7.5.5",
+ "targets": [
+ {
+ "alias": "$col",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "host_hostname"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "previous"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "sense_o11y",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT mean(\"cpu_idle\") AS \"Idle\", mean(\"cpu_iowait\"), mean(\"cpu_sys\"), mean(\"cpu_usr\") FROM \"sense_o11y\" WHERE (\"host_hostname\" = 'docker02.tylephony.com') AND $timeFilter GROUP BY time($__interval), \"host_hostname\" fill(previous)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "mem_used"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "Used"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "mem_free"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "Free"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "mem_available"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "Available"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "mem_buffers"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "Buffers"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "mem_cache"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "Cache"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "mem_shared"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "Shared"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "mem_total"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ },
+ {
+ "params": [
+ "Total"
+ ],
+ "type": "alias"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "host_hostname",
+ "operator": "=~",
+ "value": "/^$host_hostname$/"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Memory Utilization",
+ "type": "timeseries"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "dateTimeFromNow"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 5,
+ "x": 0,
+ "y": 25
+ },
+ "id": 107,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "text": {},
+ "textMode": "auto"
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ }
+ ],
+ "measurement": "sense_event",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "time_epoch"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "event_type",
+ "operator": "=",
+ "value": "hello"
+ }
+ ]
+ }
+ ],
+ "title": "Collector Socket Refresh Connection",
+ "type": "stat"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "dark-green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 7,
+ "x": 5,
+ "y": 25
+ },
+ "id": 9,
+ "interval": "$interval",
+ "options": {
+ "colorMode": "background",
+ "graphMode": "area",
+ "justifyMode": "center",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "sum"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "text": {},
+ "textMode": "auto"
+ },
+ "pluginVersion": "8.0.6",
+ "repeat": null,
+ "targets": [
+ {
+ "alias": "$tag_source",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "source"
+ ],
+ "type": "tag"
+ }
+ ],
+ "measurement": "sense_o11y",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "time_epoch"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "count"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "type",
+ "operator": "=",
+ "value": "event"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Collector Starts",
+ "transformations": [],
+ "type": "stat"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "align": "center",
+ "displayMode": "auto",
+ "filterable": false
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "dateTimeAsUS"
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byType",
+ "options": "number"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "dateTimeFromNow"
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 12,
+ "x": 12,
+ "y": 25
+ },
+ "id": 8,
+ "options": {
+ "frameIndex": 3,
+ "showHeader": true,
+ "sortBy": [
+ {
+ "desc": true,
+ "displayName": "Time"
+ }
+ ]
+ },
+ "pluginVersion": "8.0.6",
+ "repeat": null,
+ "repeatDirection": "v",
+ "targets": [
+ {
+ "alias": "$tag_source",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "source"
+ ],
+ "type": "tag"
+ }
+ ],
+ "measurement": "sense_o11y",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "time_epoch"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "distinct"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "type",
+ "operator": "=",
+ "value": "event"
+ },
+ {
+ "condition": "AND",
+ "key": "type",
+ "operator": "=",
+ "value": "event"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Collector Start Time",
+ "transformations": [
+ {
+ "id": "seriesToColumns",
+ "options": {}
+ }
+ ],
+ "type": "table"
+ }
+ ],
+ "refresh": "1m",
+ "schemaVersion": 30,
+ "style": "dark",
+ "tags": [
+ "sense-collector",
+ "influxdb"
+ ],
+ "templating": {
+ "list": [
+ {
+ "allValue": null,
+ "current": {},
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "definition": "select DISTINCT(\"host_hostname\") from (select \"host_hostname\",\"cpu_soft\" from \"sense_o11y\" WHERE $timeFilter)",
+ "description": null,
+ "error": null,
+ "hide": 0,
+ "includeAll": true,
+ "label": "Host",
+ "multi": true,
+ "name": "host_hostname",
+ "options": [],
+ "query": "select DISTINCT(\"host_hostname\") from (select \"host_hostname\",\"cpu_soft\" from \"sense_o11y\" WHERE $timeFilter)",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 5,
+ "tagValuesQuery": "",
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ },
+ {
+ "allValue": null,
+ "current": {},
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "definition": "SHOW TAG VALUES FROM \"sense_o11y\" WITH KEY = \"source\"",
+ "description": null,
+ "error": null,
+ "hide": 0,
+ "includeAll": false,
+ "label": "Source",
+ "multi": false,
+ "name": "source",
+ "options": [],
+ "query": "SHOW TAG VALUES FROM \"sense_o11y\" WITH KEY = \"source\"",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 5,
+ "type": "query"
+ },
+ {
+ "allValue": null,
+ "current": {},
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "definition": "SHOW TAG VALUES FROM \"sense_o11y\" WITH KEY = \"function\" WHERE \"source\" =~ /^$source$/",
+ "description": null,
+ "error": null,
+ "hide": 0,
+ "includeAll": true,
+ "label": "Function",
+ "multi": true,
+ "name": "function",
+ "options": [],
+ "query": "SHOW TAG VALUES FROM \"sense_o11y\" WITH KEY = \"function\" WHERE \"source\" =~ /^$source$/",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 5,
+ "type": "query"
+ },
+ {
+ "allValue": null,
+ "current": {
+ "selected": false,
+ "text": "1m",
+ "value": "1m"
+ },
+ "description": null,
+ "error": null,
+ "hide": 0,
+ "includeAll": false,
+ "label": "Interval",
+ "multi": false,
+ "name": "interval",
+ "options": [
+ {
+ "selected": false,
+ "text": "1s",
+ "value": "1s"
+ },
+ {
+ "selected": false,
+ "text": "5s",
+ "value": "5s"
+ },
+ {
+ "selected": false,
+ "text": "10s",
+ "value": "10s"
+ },
+ {
+ "selected": false,
+ "text": "15s",
+ "value": "15s"
+ },
+ {
+ "selected": false,
+ "text": "30s",
+ "value": "30s"
+ },
+ {
+ "selected": true,
+ "text": "1m",
+ "value": "1m"
+ },
+ {
+ "selected": false,
+ "text": "5m",
+ "value": "5m"
+ },
+ {
+ "selected": false,
+ "text": "10m",
+ "value": "10m"
+ },
+ {
+ "selected": false,
+ "text": "15m",
+ "value": "15m"
+ },
+ {
+ "selected": false,
+ "text": "1h",
+ "value": "1h"
+ },
+ {
+ "selected": false,
+ "text": "3h",
+ "value": "3h"
+ }
+ ],
+ "query": "1s,5s,10s,15s,30s,1m,5m,10m,15m,1h,3h",
+ "queryValue": "",
+ "skipUrlSync": false,
+ "type": "custom"
+ }
+ ]
+ },
+ "time": {
+ "from": "now/d",
+ "to": "now"
+ },
+ "timepicker": {},
+ "timezone": "",
+ "title": "Sense Collector - Collector Info",
+ "uid": "lux4rd0labs_sense_01",
+ "version": 1,
+ "description": "Sense Collector provides a way of collecting real-time data from the Sense Energy Monitor. These Grafana dashboards offer visualizations for detected devices and smart plugs and their wattage, voltage, and amp utilization."
+}
\ No newline at end of file
diff --git a/grafana/shared/sense_collector-device_overview.json b/grafana/shared/sense_collector-device_overview.json
new file mode 100644
index 0000000..7802add
--- /dev/null
+++ b/grafana/shared/sense_collector-device_overview.json
@@ -0,0 +1,2471 @@
+{
+ "__inputs": [
+ {
+ "name": "DS_INFLUXDB_- SENSE",
+ "label": "InfluxDB - sense",
+ "description": "",
+ "type": "datasource",
+ "pluginId": "influxdb",
+ "pluginName": "InfluxDB"
+ }
+ ],
+ "__requires": [
+ {
+ "type": "panel",
+ "id": "digrich-bubblechart-panel",
+ "name": "Bubble Chart",
+ "version": "1.2.0"
+ },
+ {
+ "type": "grafana",
+ "id": "grafana",
+ "name": "Grafana",
+ "version": "8.0.6"
+ },
+ {
+ "type": "datasource",
+ "id": "influxdb",
+ "name": "InfluxDB",
+ "version": "1.0.0"
+ },
+ {
+ "type": "panel",
+ "id": "state-timeline",
+ "name": "State timeline",
+ "version": ""
+ },
+ {
+ "type": "panel",
+ "id": "table",
+ "name": "Table",
+ "version": ""
+ },
+ {
+ "type": "panel",
+ "id": "timeseries",
+ "name": "Time series",
+ "version": ""
+ }
+ ],
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "target": {
+ "limit": 100,
+ "matchAny": false,
+ "tags": [],
+ "type": "dashboard"
+ },
+ "type": "dashboard"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "enable": false,
+ "iconColor": "#e35431",
+ "name": "Device Status - On",
+ "query": "SELECT body,device_state FROM \"sense_event\" WHERE (\"event_type\" = 'new_timeline' AND \"name\" =~ /^$devices$/) and $timeFilter and device_state='DeviceOn'",
+ "tagsColumn": "device_state",
+ "textColumn": "body"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "enable": false,
+ "iconColor": "#707070",
+ "name": "Device Status - Off",
+ "query": "SELECT body,device_state FROM \"sense_event\" WHERE (\"event_type\" = 'new_timeline' AND \"name\" =~ /^$devices$/) and $timeFilter and device_state='DeviceOff'",
+ "tagsColumn": "device_state",
+ "textColumn": "body"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "enable": false,
+ "iconColor": "#77d38e",
+ "name": "Device Status - Idle",
+ "query": "SELECT body,device_state FROM \"sense_event\" WHERE (\"event_type\" = 'new_timeline' AND \"name\" =~ /^$devices$/) and $timeFilter and device_state='DeviceIdle'",
+ "tagsColumn": "device_state",
+ "textColumn": "body"
+ }
+ ]
+ },
+ "description": "Sense Collector provides a way of collecting real-time data from the Sense Energy Monitor. These Grafana dashboards offer visualizations for detected devices and smart plugs and their wattage, voltage, and amp utilization.",
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "id": null,
+ "iteration": 1626667131144,
+ "links": [
+ {
+ "asDropdown": true,
+ "icon": "external link",
+ "includeVars": true,
+ "keepTime": true,
+ "tags": [
+ "sense-collector",
+ "influxdb"
+ ],
+ "targetBlank": false,
+ "title": "Sense Collector - Dashboards",
+ "tooltip": "",
+ "type": "dashboards",
+ "url": ""
+ }
+ ],
+ "panels": [
+ {
+ "bgColor": null,
+ "colorScheme": "Gradient",
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "decimal": 2,
+ "displayLabel": true,
+ "format": "watt",
+ "gradientColors": [
+ "#468860",
+ "#e35431"
+ ],
+ "gradientThresholds": "40,80",
+ "gridPos": {
+ "h": 15,
+ "w": 6,
+ "x": 0,
+ "y": 0
+ },
+ "groupDepthColors": [
+ "#E0B400",
+ "#5794F2"
+ ],
+ "groupSeperator": ",",
+ "height": 400,
+ "hideTimeOverride": true,
+ "id": 12,
+ "interval": "$interval",
+ "maxDataPoints": 1,
+ "mode": "time",
+ "nullPointMode": "connected",
+ "pluginVersion": "8.0.4",
+ "svgBubbleId": "svg_12",
+ "svgContainer": {},
+ "targets": [
+ {
+ "alias": "$tag_name",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "name"
+ ],
+ "type": "tag"
+ }
+ ],
+ "measurement": "sense_devices",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT mean(\"value\") FROM \"devices\" WHERE (\"name\" =~ /^$Device$/) AND $timeFilter GROUP BY time(3s), \"name\" fill(previous)",
+ "rawQuery": false,
+ "refId": "B",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "current_watts"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "name",
+ "operator": "=~",
+ "value": "/^$devices$/"
+ }
+ ]
+ }
+ ],
+ "thresholdColors": [
+ "#C4162A",
+ "#FADE2A",
+ "#1F60C4"
+ ],
+ "thresholds": "33,66",
+ "timeFrom": "1m",
+ "timeShift": null,
+ "title": "Current Wattage",
+ "transparent": true,
+ "type": "digrich-bubblechart-panel",
+ "valueName": "current"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 30,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "links": [],
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "watth"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 16,
+ "w": 9,
+ "x": 6,
+ "y": 0
+ },
+ "id": 9,
+ "interval": "$interval",
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "pluginVersion": "8.0.4",
+ "targets": [
+ {
+ "alias": "$tag_name",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "name"
+ ],
+ "type": "tag"
+ }
+ ],
+ "measurement": "sense_devices",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT mean(\"value\") FROM \"devices\" WHERE (\"name\" =~ /^$Device$/) AND $timeFilter GROUP BY time(3s), \"name\" fill(previous)",
+ "rawQuery": false,
+ "refId": "B",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "current_watts"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "name",
+ "operator": "=~",
+ "value": "/^$devices$/"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Wattage By Device (Stacked)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "fillOpacity": 100,
+ "lineWidth": 0
+ },
+ "mappings": [
+ {
+ "options": {
+ "DeviceIdle": {
+ "color": "#77d38e",
+ "index": 2,
+ "text": "Idle"
+ },
+ "DeviceOff": {
+ "color": "#707070",
+ "index": 1,
+ "text": "Off"
+ },
+ "DeviceOn": {
+ "color": "#e35431",
+ "index": 0,
+ "text": "On"
+ }
+ },
+ "type": "value"
+ }
+ ],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 16,
+ "w": 9,
+ "x": 15,
+ "y": 0
+ },
+ "id": 11,
+ "interval": "$interval",
+ "options": {
+ "alignValue": "center",
+ "legend": {
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "mergeValues": true,
+ "rowHeight": 0.72,
+ "showValue": "auto",
+ "tooltip": {
+ "mode": "single"
+ }
+ },
+ "pluginVersion": "8.0.4",
+ "targets": [
+ {
+ "alias": "$tag_name",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "name"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "previous"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "sense_event",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT last(\"watts\") FROM \"sense_devices\" WHERE $timeFilter GROUP BY time($__interval), \"name\" fill(null)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "device_state"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "event_type",
+ "operator": "=",
+ "value": "new_timeline"
+ },
+ {
+ "condition": "AND",
+ "key": "name",
+ "operator": "=~",
+ "value": "/^$devices$/"
+ }
+ ]
+ }
+ ],
+ "title": "Device Status",
+ "transformations": [],
+ "type": "state-timeline"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "align": "auto",
+ "displayMode": "auto",
+ "filterable": false
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Duration"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "s"
+ },
+ {
+ "id": "custom.width",
+ "value": 75
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Monthly Percent"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "percent"
+ },
+ {
+ "id": "decimals",
+ "value": 1
+ },
+ {
+ "id": "custom.width",
+ "value": 128
+ },
+ {
+ "id": "thresholds",
+ "value": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "rgba(55, 135, 45, 0)",
+ "value": null
+ },
+ {
+ "color": "rgba(69, 135, 95, 0.1)",
+ "value": 10
+ },
+ {
+ "color": "rgba(69, 135, 95, 0.2)",
+ "value": 20
+ },
+ {
+ "color": "rgba(69, 135, 95, 0.3)",
+ "value": 30
+ },
+ {
+ "color": "rgba(69, 135, 95, 0.4)",
+ "value": 40
+ },
+ {
+ "color": "rgba(69, 135, 95, 0.5)",
+ "value": 50
+ },
+ {
+ "color": "rgba(69, 135, 95, 0.6)",
+ "value": 60
+ },
+ {
+ "color": "rgba(69, 135, 95, 0.7)",
+ "value": 70
+ },
+ {
+ "color": "rgba(69, 135, 95, 0.8)",
+ "value": 80
+ },
+ {
+ "color": "rgba(69, 135, 95, 0.9)",
+ "value": 90
+ }
+ ]
+ }
+ },
+ {
+ "id": "custom.displayMode",
+ "value": "color-background"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Watts (Avg)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "watt"
+ },
+ {
+ "id": "custom.width",
+ "value": 92
+ },
+ {
+ "id": "decimals",
+ "value": 1
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Icon"
+ },
+ "properties": [
+ {
+ "id": "custom.displayMode",
+ "value": "image"
+ },
+ {
+ "id": "mappings",
+ "value": [
+ {
+ "options": {
+ "ac": {
+ "index": 0,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04NC43LDUzLjdsLTE0LjgsNGwtNC4yLTIuNGMwLjYtMS43LDAuOS0zLjQsMC45LTUuMnMtMC40LTMuNi0wLjktNS4ybDQuMi0yLjRsMTQuOCw0bDEuNy02LjRsLTguMy0yLjIKCWw4LjItNC43TDgzLDI3LjFsLTguMiw0LjdsMi4yLTguM2wtNi40LTEuN2wtNCwxNC44TDYyLjQsMzljLTIuMy0yLjYtNS41LTQuNi05LjEtNS4zdi00LjhMNjQuMSwxOGwtNC43LTQuN2wtNi4xLDYuMVYxMGgtNi43djkuNAoJbC02LjEtNi4xTDM1LjksMThsMTAuOCwxMC44djQuOGMtMy42LDAuNy02LjcsMi43LTkuMSw1LjNsLTQuMi0yLjRsLTQtMTQuOEwyMywyMy41bDIuMiw4LjNMMTcsMjcuMWwtMy4zLDUuOGw4LjIsNC43bC04LjMsMi4yCglsMS43LDYuNGwxNC44LTRsNC4yLDIuNGMtMC42LDEuNy0wLjksMy40LTAuOSw1LjJzMC40LDMuNiwwLjksNS4yTDMwLDU3LjdsLTE0LjgtNGwtMS43LDYuNGw4LjMsMi4ybC04LjIsNC43bDMuMyw1LjhsOC4yLTQuNwoJTDIzLDc2LjVsNi40LDEuN2w0LTE0LjhsNC4yLTIuNGMyLjMsMi42LDUuNSw0LjYsOS4xLDUuM3Y0LjhMMzUuOSw4Mmw0LjcsNC43bDYuMS02LjFWOTBoNi43di05LjRsNi4xLDYuMWw0LjctNC43TDUzLjMsNzEuMXYtNC44CgljMy42LTAuNyw2LjctMi43LDkuMS01LjNsNC4yLDIuNGw0LDE0LjhsNi40LTEuN2wtMi4yLTguM2w4LjIsNC43bDMuMy01LjhsLTguMi00LjdsOC4zLTIuMkw4NC43LDUzLjd6Ii8+Cjwvc3ZnPgo="
+ },
+ "always_on": {
+ "index": 1,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04My4zLDEwSDE2LjdDMTMsMTAsMTAsMTMsMTAsMTYuN3Y2Ni43YzAsMy43LDMsNi43LDYuNyw2LjdoNjYuN2MzLjcsMCw2LjctMyw2LjctNi43VjE2LjcKCUM5MCwxMyw4NywxMCw4My4zLDEweiBNNzguMyw4MGgtNi43di05LjJDNjYuMiw3Ni40LDU4LjUsODAsNTAsODBjLTE2LjYsMC0zMC0xMy40LTMwLTMwaDYuN2MwLDEyLjksMTAuNCwyMy4zLDIzLjMsMjMuMwoJYzYuNiwwLDEyLjYtMi44LDE2LjktNy4yaC05LjF2LTYuN2wxNy4yLDB2MGMxLjgsMCwzLjMsMS41LDMuMywzLjNWODB6IE03My4zLDUwYzAtMTIuOS0xMC40LTIzLjMtMjMuMy0yMy4zCgljLTYuNiwwLTEyLjYsMi44LTE2LjksNy4yaDkuMnY2LjdsLTE3LjIsMGwwLDBjLTEuOCwwLTMuMy0xLjUtMy4zLTMuM1YyMGg2LjdsMCw5LjJDMzMuOCwyMy42LDQxLjUsMjAsNTAsMjBjMTYuNiwwLDMwLDEzLjQsMzAsMzAKCUg3My4zeiIvPgo8L3N2Zz4K"
+ },
+ "car": {
+ "index": 2,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qxe2ZpbGw6IzQ2ODg2MDt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04My4zLDU2LjdsLTUuNi0xNi44Yy0wLjYtMS43LTIuNi0zLjItNC40LTMuMkg0Ni43bC0xMy4zLDIwbC0yMC4zLDguN0MxMS40LDY2LjEsMTAsNjguMiwxMCw3MHYxMy4zaDEwLjYKCWMxLjQsMy45LDUsNi43LDkuNCw2LjdzOC0yLjgsOS40LTYuN2gyNC42YzEuNCwzLjksNSw2LjcsOS40LDYuN2M0LjMsMCw4LTIuOCw5LjQtNi43SDkwdi0yMEM5MCw1OS43LDg3LDU2LjcsODMuMyw1Ni43eiBNMzAsODMuMwoJYy0xLjgsMC0zLjMtMS41LTMuMy0zLjNzMS41LTMuMywzLjMtMy4zczMuMywxLjUsMy4zLDMuM1MzMS44LDgzLjMsMzAsODMuM3ogTTU2LjcsNTYuN0g0MS4zbDguOS0xMy4zaDYuNEw1Ni43LDU2LjdMNTYuNyw1Ni43egoJIE02My4zLDU2LjdWNDMuM2g4LjVsNC40LDEzLjNINjMuM3ogTTczLjMsODMuM2MtMS44LDAtMy4zLTEuNS0zLjMtMy4zczEuNS0zLjMsMy4zLTMuM2MxLjgsMCwzLjMsMS41LDMuMywzLjNTNzUuMiw4My4zLDczLjMsODMuMwoJeiIvPgo8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODMuMywxMHY4LjNjMCwxLjgtMS41LDMuMy0zLjMsMy4zSDM2Ljd2LTVjMC0zLjctMy02LjctNi43LTYuN0gyMHY1LjhoLTYuN2MtMS44LDAtMy4zLDEuNS0zLjMsMy4zCgljMCwxLjgsMS41LDMuMywzLjMsMy4zSDIwdjVoLTYuN2MtMS44LDAtMy4zLDEuNS0zLjMsMy4zYzAsMS44LDEuNSwzLjMsMy4zLDMuM0gyMFY0MGgxMGMzLjcsMCw2LjctMyw2LjctNi43di01SDgwCgljNS41LDAsMTAtNC41LDEwLTEwVjEwSDgzLjN6Ii8+Cjwvc3ZnPgo="
+ },
+ "computer": {
+ "index": 3,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0yMy4zLDgzLjNWOTBoMjBWMjMuM0gxNi43Yy0zLjcsMC02LjcsMy02LjcsNi43djQwYzAsMy43LDMsNi43LDYuNyw2LjdoMjB2Ni43SDIzLjN6Ii8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik04My4zLDEwSDU2LjdDNTMsMTAsNTAsMTMsNTAsMTYuN3Y2Ni43YzAsMy43LDMsNi43LDYuNyw2LjdoMjYuN2MzLjcsMCw2LjctMyw2LjctNi43VjE2LjcKCUM5MCwxMyw4NywxMCw4My4zLDEweiBNNzAsODMuM2MtMS44LDAtMy4zLTEuNS0zLjMtMy4zczEuNS0zLjMsMy4zLTMuM3MzLjMsMS41LDMuMywzLjNTNzEuOCw4My4zLDcwLDgzLjN6IE04My4zLDQzLjNINTYuN3YtNi43CgloMjYuN1Y0My4zeiBNODMuMywzMEg1Ni43di02LjdoMjYuN1YzMHoiLz4KPC9zdmc+Cg=="
+ },
+ "dishes": {
+ "index": 4,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qye2ZpbGw6IzcwNzA3MDt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xMCwzMHY1My4zYzAsMy43LDMsNi43LDYuNyw2LjdoNjYuN2MzLjcsMCw2LjctMyw2LjctNi43VjMwSDEweiBNNTAsODMuM2MtMTIuOSwwLTIzLjMtMTAuNC0yMy4zLTIzLjMKCVMzNy4xLDM2LjcsNTAsMzYuN2MxMi45LDAsMjMuMywxMC40LDIzLjMsMjMuM1M2Mi45LDgzLjMsNTAsODMuM3oiLz4KPHBhdGggY2xhc3M9InN0MSIgZD0iTTUwLDQzLjNjLTkuMiwwLTE2LjcsNy41LTE2LjcsMTYuN2MwLDkuMiw3LjUsMTYuNywxNi43LDE2LjdjOS4yLDAsMTYuNy03LjUsMTYuNy0xNi43CglDNjYuNyw1MC44LDU5LjIsNDMuMyw1MCw0My4zeiBNNTAsNzBjLTUuNSwwLTEwLTQuNS0xMC0xMGMwLTUuNSw0LjUtMTAsMTAtMTBzMTAsNC41LDEwLDEwQzYwLDY1LjUsNTUuNSw3MCw1MCw3MHoiLz4KPHBhdGggY2xhc3M9InN0MiIgZD0iTTgzLjMsMTBIMTYuN0MxMywxMCwxMCwxMywxMCwxNi43djYuN2gxMHYtNi43aDIwdjYuN2gyMHYtNi43aDYuN3Y2LjdoNi43di02LjdIODB2Ni43aDEwdi02LjcKCUM5MCwxMyw4NywxMCw4My4zLDEweiIvPgo8L3N2Zz4K"
+ },
+ "fridge": {
+ "index": 5,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik03My4zLDQzLjN2MjBoLTYuN3YtMjBIMjBWODBjMCw1LjUsNC41LDEwLDEwLDEwaDQwYzUuNSwwLDEwLTQuNSwxMC0xMFY0My4zSDczLjN6Ii8+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik02Ni43LDM2LjdWMjMuM2g2Ljd2MTMuM0g4MFYyMGMwLTUuNS00LjUtMTAtMTAtMTBIMzBjLTUuNSwwLTEwLDQuNS0xMCwxMHYxNi43SDY2Ljd6Ii8+Cjwvc3ZnPgo="
+ },
+ "garage": {
+ "index": 6,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qxe2ZpbGw6IzcwNzA3MDt9Cgkuc3Qye2ZpbGw6Izc3RDM4RTt9Cjwvc3R5bGU+Cjxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iMTAsMzYuNyAxMCw5MCAyMCw5MCAyMCw0Ni43IDgwLDQ2LjcgODAsOTAgOTAsOTAgOTAsMzYuNyAiLz4KPHBvbHlnb24gY2xhc3M9InN0MSIgcG9pbnRzPSIyNi43LDUzLjMgMjYuNyw3MCA0Ni43LDcwIDQ2LjcsNjMuMyA1My4zLDYzLjMgNTMuMyw3MCA3My4zLDcwIDczLjMsNTMuMyAiLz4KPHBvbHlnb24gY2xhc3M9InN0MiIgcG9pbnRzPSI1MCwxMCAxMCwzMCA5MCwzMCAiLz4KPC9zdmc+Cg=="
+ },
+ "heat": {
+ "index": 7,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6Izc3RDM4RTt9Cgkuc3Qye2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik01MCw3NWMtMTIsMC0yNC4yLTIuNS0zMS41LTcuNEMyMy4xLDgwLjYsMzUuNCw5MCw1MCw5MHMyNi45LTkuNCwzMS41LTIyLjRDNzQuMiw3Mi41LDYyLDc1LDUwLDc1eiIvPgo8cGF0aCBjbGFzcz0ic3QxIiBkPSJNNzYuMSw0OC40Yy0xLDQuNC0zLjIsOC41LTYuNCwxMS44bC0xLjUsMS41bC0zNi40LDBsLTEuNS0xLjVjLTMuMy0zLjMtNS41LTcuNC02LjQtMTEuOAoJYy00LjUsMi4zLTcuMiw1LjEtNy4yLDguM0MxNi43LDY0LDMxLjYsNzAsNTAsNzBzMzMuMy02LDMzLjMtMTMuM0M4My4zLDUzLjUsODAuNiw1MC43LDc2LjEsNDguNHoiLz4KPHBhdGggY2xhc3M9InN0MiIgZD0iTTMzLjksNTYuN2MtNy40LTcuNC03LjQtMTkuMywwLTI2LjdjMCw1LjUsNC41LDEwLDEwLDEwdi01LjZjMC05LjUsMy45LTE4LjEsMTAuMS0yNC40YzAsNC44LDEuOCw5LjYsNS41LDEzLjMKCWw2LjcsNi43YzcuNCw3LjQsNy40LDE5LjMsMCwyNi43TDMzLjksNTYuN3oiLz4KPC9zdmc+Cg=="
+ },
+ "modem": {
+ "index": 8,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6Izc3RDM4RTt9Cgkuc3Qye2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xMyw0MC42Yy0yLTQuNy0zLTkuOS0zLTE1LjNjMC01LjQsMS4xLTEwLjYsMy0xNS4zbDYuMiwyLjZjLTEuNiwzLjktMi41LDguMi0yLjUsMTIuOGMwLDQuNSwwLjksOC44LDIuNSwxMi44CglMMTMsNDAuNnoiLz4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTgwLjgsMTIuNmMxLjYsMy45LDIuNSw4LjIsMi41LDEyLjhjMCw0LjUtMC45LDguOC0yLjUsMTIuOGw2LjIsMi42YzItNC43LDMtOS45LDMtMTUuM2MwLTUuNC0xLjEtMTAuNi0zLTE1LjMKCUw4MC44LDEyLjZ6Ii8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik02NS40LDE4LjljMC44LDIsMS4zLDQuMSwxLjMsNi40YzAsMi4zLTAuNSw0LjQtMS4zLDYuNGw2LjIsMi42YzEuMS0yLjgsMS44LTUuOCwxLjgtOC45cy0wLjYtNi4yLTEuOC04LjkKCUw2NS40LDE4Ljl6Ii8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0zNC42LDMxLjdjLTAuOC0yLTEuMy00LjEtMS4zLTYuNGMwLTIuMywwLjUtNC40LDEuMy02LjRsLTYuMi0yLjZjLTEuMSwyLjgtMS44LDUuOC0xLjgsOC45czAuNiw2LjIsMS44LDguOQoJTDM0LjYsMzEuN3oiLz4KPHBhdGggY2xhc3M9InN0MiIgZD0iTTgzLjMsNTBoLTMwVjM0LjdjMy45LTEuNCw2LjctNSw2LjctOS40YzAtNS41LTQuNS0xMC0xMC0xMGMtNS41LDAtMTAsNC41LTEwLDEwYzAsNC40LDIuOCw4LDYuNyw5LjRWNTBoLTMwCglDMTMsNTAsMTAsNTMsMTAsNTYuN3YyMGMwLDMuNywzLDYuNyw2LjcsNi43VjkwaDYuN3YtNi43aDIwdi0yMEg1MHYyMGg2Ljd2LTIwaDYuN3YyMEg3MHYtMjBoNi43djIwVjkwaDYuN3YtNi43CgljMy43LDAsNi43LTMsNi43LTYuN3YtMjBDOTAsNTMsODcsNTAsODMuMyw1MHogTTM2LjcsNzBIMjMuM3YtNi43aDEzLjNWNzB6Ii8+Cjwvc3ZnPgo="
+ },
+ "outlet": {
+ "index": 9,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik03Ny42LDIwbC01NS4yLDBjLTE2LjYsMTYuNi0xNi42LDQzLjQsMCw2MGw1NS4yLDBDOTQuMSw2My40LDk0LjEsMzYuNiw3Ny42LDIweiBNMzYuNyw1My4zSDMwdi0yMGg2LjdWNTMuM3oKCSBNNTYuNyw2Ni43SDQzLjNWNjBoMTMuM1Y2Ni43eiBNNzAsNTMuM2gtNi43di0yMEg3MFY1My4zeiIvPgo8L3N2Zz4K"
+ },
+ "settings": {
+ "index": 10,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04OS44LDUzLjNjMC4xLTEuMSwwLjItMi4yLDAuMi0zLjNjMC0xLjEtMC4xLTIuMi0wLjItMy4zaC02LjdjLTAuMi0yLjItMC42LTQuMy0xLjItNi4zbDYuMi0yLjYKCWMtMC43LTIuMS0xLjYtNC4yLTIuNi02LjFsLTYuMiwyLjZjLTEtMS45LTIuMi0zLjctMy42LTUuM2w0LjctNC43Yy0xLjQtMS43LTMtMy4zLTQuNy00LjdsLTQuNyw0LjdjLTEuNi0xLjQtMy40LTIuNS01LjMtMy42CglsMi42LTYuMmMtMi0xLTQtMS45LTYuMS0yLjZsLTIuNiw2LjJjLTItMC42LTQuMS0xLTYuMy0xLjJ2LTYuN0M1Mi4yLDEwLjEsNTEuMSwxMCw1MCwxMGMtMS4xLDAtMi4yLDAuMS0zLjMsMC4ydjYuNwoJYy0yLjIsMC4yLTQuMywwLjYtNi4zLDEuMmwtMi42LTYuMmMtMi4xLDAuNy00LjIsMS42LTYuMSwyLjZsMi42LDYuMmMtMS45LDEtMy43LDIuMi01LjMsMy42bC00LjctNC43Yy0xLjcsMS40LTMuMywzLTQuNyw0LjcKCWw0LjcsNC43Yy0xLjMsMS43LTIuNSwzLjQtMy42LDUuM2wtNi4yLTIuNmMtMSwyLTEuOSw0LTIuNiw2LjFsNi4yLDIuNmMtMC42LDItMSw0LjEtMS4yLDYuM2gtNi43QzEwLjEsNDcuOCwxMCw0OC45LDEwLDUwCgljMCwxLjEsMC4xLDIuMiwwLjIsMy4zaDYuN2MwLjIsMi4yLDAuNiw0LjMsMS4yLDYuM2wtNi4yLDIuNmMwLjcsMi4xLDEuNiw0LjIsMi42LDYuMWw2LjItMi42aDBjMSwxLjksMi4yLDMuNywzLjYsNS4zbDAsMAoJbC00LjcsNC43YzEuNCwxLjcsMywzLjMsNC43LDQuN2w0LjctNC43YzEuNiwxLjQsMy40LDIuNSw1LjMsMy42djBsLTIuNiw2LjJjMiwxLDQsMS45LDYuMSwyLjZsMi42LTYuMnYwYzIsMC42LDQuMSwxLDYuMywxLjN2Ni43CgljMS4xLDAuMSwyLjIsMC4yLDMuMywwLjJjMS4xLDAsMi4yLTAuMSwzLjMtMC4ydi02LjdjMi4yLTAuMiw0LjMtMC42LDYuMy0xLjNsMi42LDYuMmMyLjEtMC43LDQuMi0xLjYsNi4yLTIuNmwtMi42LTYuMgoJYzEuOS0xLDMuNy0yLjIsNS4zLTMuNmw0LjcsNC43YzEuNy0xLjQsMy4zLTMsNC43LTQuN2wtNC43LTQuN2MxLjQtMS42LDIuNS0zLjQsMy42LTUuM2gwbDYuMiwyLjZjMS0yLDEuOS00LDIuNi02LjFsLTYuMi0yLjYKCWMwLjYtMiwxLTQuMSwxLjMtNi4zTDg5LjgsNTMuM0w4OS44LDUzLjN6IE0zMCw1MGMwLTEwLjYsOC4zLTE5LjMsMTguNy0xOS45TDQwLDUzLjNoMTAuNGwtNS45LDE1LjlDMzYuMSw2Ni44LDMwLDU5LjEsMzAsNTB6CgkgTTUxLjMsNjkuOUw2MCw0Ni43SDQ5LjZsNi0xNS45QzYzLjksMzMuMiw3MCw0MC45LDcwLDUwQzcwLDYwLjYsNjEuNyw2OS4zLDUxLjMsNjkuOXoiLz4KPC9zdmc+Cg=="
+ },
+ "socket": {
+ "index": 11,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+Cjxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iNTguMywyNi43IDU4LjMsMTAgNTEuNywxMCA1MS43LDI2LjcgMzUsMjYuNyAzNSwxMCAyOC4zLDEwIDI4LjMsMjYuNyAxNi43LDI2LjcgMTYuNyw0MCA3MCw0MCAKCTcwLDI2LjcgIi8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik01My4zLDgzLjNjLTMuNywwLTYuNy0zLTYuNy02LjdoMTBsNi43LTMwaC00MGw2LjcsMzBoMTBDNDAsODQsNDYsOTAsNTMuMyw5MGgzMHYtNi43SDUzLjN6Ii8+Cjwvc3ZnPgo="
+ },
+ "toaster_oven": {
+ "index": 12,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qye2ZpbGw6I0I3QjdCNzt9Cgkuc3Qze2ZpbGw6IzcwNzA3MDt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0yMy4zLDQwVjI2LjdjMC0xLjgsMS41LTMuMywzLjMtMy4zaDQwYzEuOCwwLDMuMywxLjUsMy4zLDMuM1Y0MEgyMy4zeiIvPgo8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODMuMyw1NXYtMy4zYzAtMy43LTMtNi43LTYuNy02LjdoLTYwQzEzLDQ1LDEwLDQ4LDEwLDUxLjd2MzEuN2g2LjdWOTBoNi43di02LjdINzBWOTBoNi43di02LjdoNi43di0yMEw5MCw2MAoJdi01SDgzLjN6IE01Ni43LDczLjNjLTEuOCwwLTMuMy0xLjUtMy4zLTMuM2MwLTEuOCwxLjUtMy4zLDMuMy0zLjNjMS44LDAsMy4zLDEuNSwzLjMsMy4zQzYwLDcxLjgsNTguNSw3My4zLDU2LjcsNzMuM3ogTTcwLDczLjMKCWMtMS44LDAtMy4zLTEuNS0zLjMtMy4zYzAtMS44LDEuNS0zLjMsMy4zLTMuM3MzLjMsMS41LDMuMywzLjNDNzMuMyw3MS44LDcxLjgsNzMuMyw3MCw3My4zeiIvPgo8cmVjdCB4PSI0My4zIiB5PSIxMCIgY2xhc3M9InN0MiIgd2lkdGg9IjYuNyIgaGVpZ2h0PSI4LjMiLz4KPHJlY3QgeD0iMzAiIHk9IjEwIiBjbGFzcz0ic3QzIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjguMyIvPgo8cmVjdCB4PSI1Ni43IiB5PSIxMCIgY2xhc3M9InN0MyIgd2lkdGg9IjYuNyIgaGVpZ2h0PSI4LjMiLz4KPC9zdmc+Cg=="
+ },
+ "trash": {
+ "index": 13,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzcwNzA3MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qye2ZpbGw6I0I3QjdCNzt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik05MCwzNi43VjMwYzAtMy43LTMtNi43LTYuNy02LjdIMTYuN2MtMy43LDAtNi43LDMtNi43LDYuN3Y2LjdIOTB6Ii8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0xNi43LDQzLjN2NDBjMCwzLjcsMyw2LjcsNi43LDYuN2g1My4zYzMuNywwLDYuNy0zLDYuNy02Ljd2LTQwSDE2Ljd6IE0zNi43LDgwSDMwVjUzLjNoNi43VjgweiBNNTMuMyw4MGgtNi43CglWNTMuM2g2LjdWODB6IE03MCw4MGgtNi43VjUzLjNINzBWODB6Ii8+CjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik02MCwxMEg0MGMtMy43LDAtNi43LDMtNi43LDYuN2gzMy4zQzY2LjcsMTMsNjMuNywxMCw2MCwxMHoiLz4KPC9zdmc+Cg=="
+ },
+ "tv": {
+ "index": 14,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6Izc3RDM4RTt9Cgkuc3Qxe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qye2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik03MCw3OC4zVjkwaDEzLjNjMy43LDAsNi43LTMsNi43LTYuN3YtNUg3MHoiLz4KPHBhdGggY2xhc3M9InN0MSIgZD0iTTgzLjMsMzYuN0g3MHYzNi43aDIwdi0zMEM5MCwzOS43LDg3LDM2LjcsODMuMywzNi43eiBNODAsNjMuM2MtMS44LDAtMy4zLTEuNS0zLjMtMy4zczEuNS0zLjMsMy4zLTMuMwoJYzEuOCwwLDMuMywxLjUsMy4zLDMuM1M4MS44LDYzLjMsODAsNjMuM3ogTTgwLDUzLjNjLTEuOCwwLTMuMy0xLjUtMy4zLTMuM3MxLjUtMy4zLDMuMy0zLjNjMS44LDAsMy4zLDEuNSwzLjMsMy4zCglTODEuOCw1My4zLDgwLDUzLjN6Ii8+CjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik02OCwxNC43TDYzLjMsMTBMNDAsMzMuM0wxNi43LDEwTDEyLDE0LjdsMjEuOSwyMkgxNi43Yy0zLjcsMC02LjcsMy02LjcsNi43djQwYzAsMy43LDMsNi43LDYuNyw2LjdINjVWMzYuNwoJSDQ2LjFMNjgsMTQuN3oiLz4KPC9zdmc+Cg=="
+ },
+ "voice_assistant": {
+ "index": 15,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik01MCwxMEwyMCwyNi43djQ2LjdMNTAsOTBsMzAtMTYuN1YyNi43TDUwLDEweiBNNDYuNyw4MC41bC0yMC0xMS4xdi00LjdsMjAsMTEuMVY4MC41eiBNNDYuNyw2OC4ybC0yMC0xMS4xCgl2LTcuNmwyMCwxMS4xVjY4LjJ6IE00Ni43LDUyLjlsLTIwLTExLjF2LTcuNmwyMCwxMS4xVjUyLjl6IE01MCwzNC44Yy01LjUsMC0xMC0yLjgtMTAtNi4yYzAtMy41LDQuNS02LjIsMTAtNi4yCgljNS41LDAsMTAsMi44LDEwLDYuMkM2MCwzMiw1NS41LDM0LjgsNTAsMzQuOHogTTY2LjcsNTMuMUw2MCw1Ni44di03LjZsNi43LTMuN1Y1My4xeiIvPgo8L3N2Zz4K"
+ },
+ "washer": {
+ "index": 16,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qye2ZpbGw6IzcwNzA3MDt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xMCwzMHY1My4zYzAsMy43LDMsNi43LDYuNyw2LjdoNjYuN2MzLjcsMCw2LjctMyw2LjctNi43VjMwSDEweiBNNTAsODMuM2MtMTIuOSwwLTIzLjMtMTAuNC0yMy4zLTIzLjMKCVMzNy4xLDM2LjcsNTAsMzYuN2MxMi45LDAsMjMuMywxMC40LDIzLjMsMjMuM1M2Mi45LDgzLjMsNTAsODMuM3oiLz4KPHBhdGggY2xhc3M9InN0MSIgZD0iTTUwLDQzLjNjLTkuMiwwLTE2LjcsNy41LTE2LjcsMTYuN2MwLDkuMiw3LjUsMTYuNywxNi43LDE2LjdjOS4yLDAsMTYuNy03LjUsMTYuNy0xNi43CglDNjYuNyw1MC44LDU5LjIsNDMuMyw1MCw0My4zeiBNNTAsNzBjLTUuNSwwLTEwLTQuNS0xMC0xMGMwLTUuNSw0LjUtMTAsMTAtMTBzMTAsNC41LDEwLDEwQzYwLDY1LjUsNTUuNSw3MCw1MCw3MHoiLz4KPHBhdGggY2xhc3M9InN0MiIgZD0iTTgzLjMsMTBIMTYuN0MxMywxMCwxMCwxMywxMCwxNi43djYuN2gxMHYtNi43aDIwdjYuN2gyMHYtNi43aDYuN3Y2LjdoNi43di02LjdIODB2Ni43aDEwdi02LjcKCUM5MCwxMyw4NywxMCw4My4zLDEweiIvPgo8L3N2Zz4K"
+ },
+ "water_heater": {
+ "index": 17,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzcwNzA3MDt9Cgkuc3Qxe2ZpbGw6I0I3QjdCNzt9Cgkuc3Qye2ZpbGw6Izc3RDM4RTt9Cgkuc3Qze2ZpbGw6IzQ2ODg2MDt9Cgkuc3Q0e2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxyZWN0IHg9IjI2LjciIHk9IjcwIiBjbGFzcz0ic3QwIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjIwIi8+CjxyZWN0IHg9IjI2LjciIHk9IjUwIiBjbGFzcz0ic3QxIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjEzLjMiLz4KPHJlY3QgeD0iNDAiIHk9IjUwIiBjbGFzcz0ic3QyIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjIwIi8+CjxyZWN0IHg9IjQwIiB5PSI3Ni43IiBjbGFzcz0ic3QxIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjYuNyIvPgo8cmVjdCB4PSI1My4zIiB5PSI1MCIgY2xhc3M9InN0MCIgd2lkdGg9IjYuNyIgaGVpZ2h0PSI2LjciLz4KPHJlY3QgeD0iNTMuMyIgeT0iNjMuMyIgY2xhc3M9InN0MSIgd2lkdGg9IjYuNyIgaGVpZ2h0PSIxMy4zIi8+CjxyZWN0IHg9IjUzLjMiIHk9IjgzLjMiIGNsYXNzPSJzdDIiIHdpZHRoPSI2LjciIGhlaWdodD0iNi43Ii8+CjxyZWN0IHg9IjY2LjciIHk9IjUwIiBjbGFzcz0ic3QzIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjEzLjMiLz4KPHJlY3QgeD0iNjYuNyIgeT0iNzAiIGNsYXNzPSJzdDEiIHdpZHRoPSI2LjciIGhlaWdodD0iMjAiLz4KPHBhdGggY2xhc3M9InN0NCIgZD0iTTUzLjMsMjAuM1YxMGgtNi43djEwLjNjLTExLjMsMS42LTIwLDExLjMtMjAsMjMuMWg0Ni43QzczLjMsMzEuNiw2NC42LDIxLjksNTMuMywyMC4zeiIvPgo8L3N2Zz4K"
+ }
+ },
+ "type": "value"
+ }
+ ]
+ },
+ {
+ "id": "custom.align",
+ "value": "center"
+ },
+ {
+ "id": "custom.width",
+ "value": 55
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "State Time"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "dateTimeAsUS"
+ },
+ {
+ "id": "custom.align",
+ "value": "center"
+ },
+ {
+ "id": "custom.width",
+ "value": 183
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Monthly kWh"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "kwatth"
+ },
+ {
+ "id": "decimals",
+ "value": 2
+ },
+ {
+ "id": "custom.width",
+ "value": 104
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Watts (In Use)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "watt"
+ },
+ {
+ "id": "custom.width",
+ "value": 120
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "State"
+ },
+ "properties": [
+ {
+ "id": "custom.displayMode",
+ "value": "color-background"
+ },
+ {
+ "id": "mappings",
+ "value": [
+ {
+ "options": {
+ "Idle": {
+ "color": "#77d38e",
+ "index": 2
+ },
+ "Off": {
+ "color": "#707070",
+ "index": 1
+ },
+ "On": {
+ "color": "#e35431",
+ "index": 0
+ }
+ },
+ "type": "value"
+ },
+ {
+ "options": {
+ "match": "null+nan",
+ "result": {
+ "color": "transparent",
+ "index": 3
+ }
+ },
+ "type": "special"
+ }
+ ]
+ },
+ {
+ "id": "custom.align",
+ "value": "center"
+ },
+ {
+ "id": "custom.width",
+ "value": 55
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Monthly Runs"
+ },
+ "properties": [
+ {
+ "id": "custom.width",
+ "value": 104
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Watts (AO)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "watt"
+ },
+ {
+ "id": "custom.width",
+ "value": 90
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 12,
+ "w": 13,
+ "x": 0,
+ "y": 16
+ },
+ "hideTimeOverride": true,
+ "id": 5,
+ "maxDataPoints": 1,
+ "options": {
+ "showHeader": true,
+ "sortBy": [
+ {
+ "desc": true,
+ "displayName": "Watts (In Use)"
+ }
+ ]
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "query": "SELECT last(*) FROM \"sense_devices\" WHERE (\"name\" =~ /^$devices$/) and $timeFilter GROUP BY time(1d), \"name\" fill(null)",
+ "rawQuery": true,
+ "refId": "A",
+ "resultFormat": "table"
+ }
+ ],
+ "timeFrom": "5m",
+ "timeShift": null,
+ "title": "Device Details - Average",
+ "transformations": [
+ {
+ "id": "organize",
+ "options": {
+ "excludeByName": {
+ "Time": true,
+ "last_avg_monthly_cost": true,
+ "last_current_ao_wattage": true,
+ "last_current_month_KWH": true,
+ "last_current_month_cost": true,
+ "last_current_month_runs": true,
+ "last_e": true,
+ "last_i": true,
+ "last_icon": false,
+ "last_last_state": false,
+ "last_last_state_time_epoch": false,
+ "last_v": true,
+ "last_watts": false,
+ "last_yearly_KWH": true,
+ "last_yearly_cost": true,
+ "last_yearly_text": true
+ },
+ "indexByName": {
+ "Time": 0,
+ "last_ao_w": 7,
+ "last_avg_duration": 8,
+ "last_avg_monthly_KWH": 9,
+ "last_avg_monthly_cost": 10,
+ "last_avg_monthly_pct": 11,
+ "last_avg_monthly_runs": 12,
+ "last_avg_watts": 6,
+ "last_current_ao_wattage": 13,
+ "last_current_month_KWH": 14,
+ "last_current_month_cost": 15,
+ "last_current_month_runs": 16,
+ "last_current_watts": 5,
+ "last_e": 20,
+ "last_i": 21,
+ "last_icon": 2,
+ "last_last_state": 3,
+ "last_last_state_time_epoch": 4,
+ "last_v": 22,
+ "last_yearly_KWH": 17,
+ "last_yearly_cost": 18,
+ "last_yearly_text": 19,
+ "name": 1
+ },
+ "renameByName": {
+ "last_ao_w": "Watts (AO)",
+ "last_avg_duration": "Duration",
+ "last_avg_monthly_KWH": "Monthly kWh",
+ "last_avg_monthly_cost": "Monthly Cost",
+ "last_avg_monthly_pct": "Monthly Percent",
+ "last_avg_monthly_runs": "Monthly Runs",
+ "last_avg_watts": "Watts (Avg)",
+ "last_current_ao_wattage": "Always On Wattage (Average)",
+ "last_current_month_KWH": "KWH (Current Month)",
+ "last_current_month_cost": "Cost (Current Month)",
+ "last_current_month_runs": "Runs (Current Month)",
+ "last_current_watts": "Watts (In Use)",
+ "last_e": "Who Knows",
+ "last_i": "Amps",
+ "last_icon": "Icon",
+ "last_last_state": "State",
+ "last_last_state_time_epoch": "State Time",
+ "last_v": "Voltage",
+ "last_watts": "Watts",
+ "last_yearly_KWH": "KWH (Yearly)",
+ "last_yearly_cost": "Cost (Yearly)",
+ "last_yearly_text": "Text (Yearly)",
+ "name": "Name"
+ }
+ }
+ }
+ ],
+ "type": "table"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "align": "auto",
+ "displayMode": "auto",
+ "filterable": false
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Duration (Average)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "s"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Monthly Percent (Average)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "percent"
+ },
+ {
+ "id": "decimals",
+ "value": 1
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Watts (Average)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "watt"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "KWH (Yearly)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "kwatth"
+ },
+ {
+ "id": "decimals",
+ "value": 2
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Cost"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "currencyUSD"
+ },
+ {
+ "id": "custom.width",
+ "value": 59
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Cost (Yearly)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "currencyUSD"
+ },
+ {
+ "id": "decimals",
+ "value": 2
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Icon"
+ },
+ "properties": [
+ {
+ "id": "custom.displayMode",
+ "value": "image"
+ },
+ {
+ "id": "mappings",
+ "value": [
+ {
+ "options": {
+ "ac": {
+ "index": 0,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04NC43LDUzLjdsLTE0LjgsNGwtNC4yLTIuNGMwLjYtMS43LDAuOS0zLjQsMC45LTUuMnMtMC40LTMuNi0wLjktNS4ybDQuMi0yLjRsMTQuOCw0bDEuNy02LjRsLTguMy0yLjIKCWw4LjItNC43TDgzLDI3LjFsLTguMiw0LjdsMi4yLTguM2wtNi40LTEuN2wtNCwxNC44TDYyLjQsMzljLTIuMy0yLjYtNS41LTQuNi05LjEtNS4zdi00LjhMNjQuMSwxOGwtNC43LTQuN2wtNi4xLDYuMVYxMGgtNi43djkuNAoJbC02LjEtNi4xTDM1LjksMThsMTAuOCwxMC44djQuOGMtMy42LDAuNy02LjcsMi43LTkuMSw1LjNsLTQuMi0yLjRsLTQtMTQuOEwyMywyMy41bDIuMiw4LjNMMTcsMjcuMWwtMy4zLDUuOGw4LjIsNC43bC04LjMsMi4yCglsMS43LDYuNGwxNC44LTRsNC4yLDIuNGMtMC42LDEuNy0wLjksMy40LTAuOSw1LjJzMC40LDMuNiwwLjksNS4yTDMwLDU3LjdsLTE0LjgtNGwtMS43LDYuNGw4LjMsMi4ybC04LjIsNC43bDMuMyw1LjhsOC4yLTQuNwoJTDIzLDc2LjVsNi40LDEuN2w0LTE0LjhsNC4yLTIuNGMyLjMsMi42LDUuNSw0LjYsOS4xLDUuM3Y0LjhMMzUuOSw4Mmw0LjcsNC43bDYuMS02LjFWOTBoNi43di05LjRsNi4xLDYuMWw0LjctNC43TDUzLjMsNzEuMXYtNC44CgljMy42LTAuNyw2LjctMi43LDkuMS01LjNsNC4yLDIuNGw0LDE0LjhsNi40LTEuN2wtMi4yLTguM2w4LjIsNC43bDMuMy01LjhsLTguMi00LjdsOC4zLTIuMkw4NC43LDUzLjd6Ii8+Cjwvc3ZnPgo="
+ },
+ "always_on": {
+ "index": 1,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04My4zLDEwSDE2LjdDMTMsMTAsMTAsMTMsMTAsMTYuN3Y2Ni43YzAsMy43LDMsNi43LDYuNyw2LjdoNjYuN2MzLjcsMCw2LjctMyw2LjctNi43VjE2LjcKCUM5MCwxMyw4NywxMCw4My4zLDEweiBNNzguMyw4MGgtNi43di05LjJDNjYuMiw3Ni40LDU4LjUsODAsNTAsODBjLTE2LjYsMC0zMC0xMy40LTMwLTMwaDYuN2MwLDEyLjksMTAuNCwyMy4zLDIzLjMsMjMuMwoJYzYuNiwwLDEyLjYtMi44LDE2LjktNy4yaC05LjF2LTYuN2wxNy4yLDB2MGMxLjgsMCwzLjMsMS41LDMuMywzLjNWODB6IE03My4zLDUwYzAtMTIuOS0xMC40LTIzLjMtMjMuMy0yMy4zCgljLTYuNiwwLTEyLjYsMi44LTE2LjksNy4yaDkuMnY2LjdsLTE3LjIsMGwwLDBjLTEuOCwwLTMuMy0xLjUtMy4zLTMuM1YyMGg2LjdsMCw5LjJDMzMuOCwyMy42LDQxLjUsMjAsNTAsMjBjMTYuNiwwLDMwLDEzLjQsMzAsMzAKCUg3My4zeiIvPgo8L3N2Zz4K"
+ },
+ "car": {
+ "index": 2,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qxe2ZpbGw6IzQ2ODg2MDt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04My4zLDU2LjdsLTUuNi0xNi44Yy0wLjYtMS43LTIuNi0zLjItNC40LTMuMkg0Ni43bC0xMy4zLDIwbC0yMC4zLDguN0MxMS40LDY2LjEsMTAsNjguMiwxMCw3MHYxMy4zaDEwLjYKCWMxLjQsMy45LDUsNi43LDkuNCw2LjdzOC0yLjgsOS40LTYuN2gyNC42YzEuNCwzLjksNSw2LjcsOS40LDYuN2M0LjMsMCw4LTIuOCw5LjQtNi43SDkwdi0yMEM5MCw1OS43LDg3LDU2LjcsODMuMyw1Ni43eiBNMzAsODMuMwoJYy0xLjgsMC0zLjMtMS41LTMuMy0zLjNzMS41LTMuMywzLjMtMy4zczMuMywxLjUsMy4zLDMuM1MzMS44LDgzLjMsMzAsODMuM3ogTTU2LjcsNTYuN0g0MS4zbDguOS0xMy4zaDYuNEw1Ni43LDU2LjdMNTYuNyw1Ni43egoJIE02My4zLDU2LjdWNDMuM2g4LjVsNC40LDEzLjNINjMuM3ogTTczLjMsODMuM2MtMS44LDAtMy4zLTEuNS0zLjMtMy4zczEuNS0zLjMsMy4zLTMuM2MxLjgsMCwzLjMsMS41LDMuMywzLjNTNzUuMiw4My4zLDczLjMsODMuMwoJeiIvPgo8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODMuMywxMHY4LjNjMCwxLjgtMS41LDMuMy0zLjMsMy4zSDM2Ljd2LTVjMC0zLjctMy02LjctNi43LTYuN0gyMHY1LjhoLTYuN2MtMS44LDAtMy4zLDEuNS0zLjMsMy4zCgljMCwxLjgsMS41LDMuMywzLjMsMy4zSDIwdjVoLTYuN2MtMS44LDAtMy4zLDEuNS0zLjMsMy4zYzAsMS44LDEuNSwzLjMsMy4zLDMuM0gyMFY0MGgxMGMzLjcsMCw2LjctMyw2LjctNi43di01SDgwCgljNS41LDAsMTAtNC41LDEwLTEwVjEwSDgzLjN6Ii8+Cjwvc3ZnPgo="
+ },
+ "computer": {
+ "index": 3,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0yMy4zLDgzLjNWOTBoMjBWMjMuM0gxNi43Yy0zLjcsMC02LjcsMy02LjcsNi43djQwYzAsMy43LDMsNi43LDYuNyw2LjdoMjB2Ni43SDIzLjN6Ii8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik04My4zLDEwSDU2LjdDNTMsMTAsNTAsMTMsNTAsMTYuN3Y2Ni43YzAsMy43LDMsNi43LDYuNyw2LjdoMjYuN2MzLjcsMCw2LjctMyw2LjctNi43VjE2LjcKCUM5MCwxMyw4NywxMCw4My4zLDEweiBNNzAsODMuM2MtMS44LDAtMy4zLTEuNS0zLjMtMy4zczEuNS0zLjMsMy4zLTMuM3MzLjMsMS41LDMuMywzLjNTNzEuOCw4My4zLDcwLDgzLjN6IE04My4zLDQzLjNINTYuN3YtNi43CgloMjYuN1Y0My4zeiBNODMuMywzMEg1Ni43di02LjdoMjYuN1YzMHoiLz4KPC9zdmc+Cg=="
+ },
+ "dishes": {
+ "index": 4,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qye2ZpbGw6IzcwNzA3MDt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xMCwzMHY1My4zYzAsMy43LDMsNi43LDYuNyw2LjdoNjYuN2MzLjcsMCw2LjctMyw2LjctNi43VjMwSDEweiBNNTAsODMuM2MtMTIuOSwwLTIzLjMtMTAuNC0yMy4zLTIzLjMKCVMzNy4xLDM2LjcsNTAsMzYuN2MxMi45LDAsMjMuMywxMC40LDIzLjMsMjMuM1M2Mi45LDgzLjMsNTAsODMuM3oiLz4KPHBhdGggY2xhc3M9InN0MSIgZD0iTTUwLDQzLjNjLTkuMiwwLTE2LjcsNy41LTE2LjcsMTYuN2MwLDkuMiw3LjUsMTYuNywxNi43LDE2LjdjOS4yLDAsMTYuNy03LjUsMTYuNy0xNi43CglDNjYuNyw1MC44LDU5LjIsNDMuMyw1MCw0My4zeiBNNTAsNzBjLTUuNSwwLTEwLTQuNS0xMC0xMGMwLTUuNSw0LjUtMTAsMTAtMTBzMTAsNC41LDEwLDEwQzYwLDY1LjUsNTUuNSw3MCw1MCw3MHoiLz4KPHBhdGggY2xhc3M9InN0MiIgZD0iTTgzLjMsMTBIMTYuN0MxMywxMCwxMCwxMywxMCwxNi43djYuN2gxMHYtNi43aDIwdjYuN2gyMHYtNi43aDYuN3Y2LjdoNi43di02LjdIODB2Ni43aDEwdi02LjcKCUM5MCwxMyw4NywxMCw4My4zLDEweiIvPgo8L3N2Zz4K"
+ },
+ "fridge": {
+ "index": 5,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik03My4zLDQzLjN2MjBoLTYuN3YtMjBIMjBWODBjMCw1LjUsNC41LDEwLDEwLDEwaDQwYzUuNSwwLDEwLTQuNSwxMC0xMFY0My4zSDczLjN6Ii8+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik02Ni43LDM2LjdWMjMuM2g2Ljd2MTMuM0g4MFYyMGMwLTUuNS00LjUtMTAtMTAtMTBIMzBjLTUuNSwwLTEwLDQuNS0xMCwxMHYxNi43SDY2Ljd6Ii8+Cjwvc3ZnPgo="
+ },
+ "garage": {
+ "index": 6,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qxe2ZpbGw6IzcwNzA3MDt9Cgkuc3Qye2ZpbGw6Izc3RDM4RTt9Cjwvc3R5bGU+Cjxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iMTAsMzYuNyAxMCw5MCAyMCw5MCAyMCw0Ni43IDgwLDQ2LjcgODAsOTAgOTAsOTAgOTAsMzYuNyAiLz4KPHBvbHlnb24gY2xhc3M9InN0MSIgcG9pbnRzPSIyNi43LDUzLjMgMjYuNyw3MCA0Ni43LDcwIDQ2LjcsNjMuMyA1My4zLDYzLjMgNTMuMyw3MCA3My4zLDcwIDczLjMsNTMuMyAiLz4KPHBvbHlnb24gY2xhc3M9InN0MiIgcG9pbnRzPSI1MCwxMCAxMCwzMCA5MCwzMCAiLz4KPC9zdmc+Cg=="
+ },
+ "heat": {
+ "index": 7,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6Izc3RDM4RTt9Cgkuc3Qye2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik01MCw3NWMtMTIsMC0yNC4yLTIuNS0zMS41LTcuNEMyMy4xLDgwLjYsMzUuNCw5MCw1MCw5MHMyNi45LTkuNCwzMS41LTIyLjRDNzQuMiw3Mi41LDYyLDc1LDUwLDc1eiIvPgo8cGF0aCBjbGFzcz0ic3QxIiBkPSJNNzYuMSw0OC40Yy0xLDQuNC0zLjIsOC41LTYuNCwxMS44bC0xLjUsMS41bC0zNi40LDBsLTEuNS0xLjVjLTMuMy0zLjMtNS41LTcuNC02LjQtMTEuOAoJYy00LjUsMi4zLTcuMiw1LjEtNy4yLDguM0MxNi43LDY0LDMxLjYsNzAsNTAsNzBzMzMuMy02LDMzLjMtMTMuM0M4My4zLDUzLjUsODAuNiw1MC43LDc2LjEsNDguNHoiLz4KPHBhdGggY2xhc3M9InN0MiIgZD0iTTMzLjksNTYuN2MtNy40LTcuNC03LjQtMTkuMywwLTI2LjdjMCw1LjUsNC41LDEwLDEwLDEwdi01LjZjMC05LjUsMy45LTE4LjEsMTAuMS0yNC40YzAsNC44LDEuOCw5LjYsNS41LDEzLjMKCWw2LjcsNi43YzcuNCw3LjQsNy40LDE5LjMsMCwyNi43TDMzLjksNTYuN3oiLz4KPC9zdmc+Cg=="
+ },
+ "modem": {
+ "index": 8,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6Izc3RDM4RTt9Cgkuc3Qye2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xMyw0MC42Yy0yLTQuNy0zLTkuOS0zLTE1LjNjMC01LjQsMS4xLTEwLjYsMy0xNS4zbDYuMiwyLjZjLTEuNiwzLjktMi41LDguMi0yLjUsMTIuOGMwLDQuNSwwLjksOC44LDIuNSwxMi44CglMMTMsNDAuNnoiLz4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTgwLjgsMTIuNmMxLjYsMy45LDIuNSw4LjIsMi41LDEyLjhjMCw0LjUtMC45LDguOC0yLjUsMTIuOGw2LjIsMi42YzItNC43LDMtOS45LDMtMTUuM2MwLTUuNC0xLjEtMTAuNi0zLTE1LjMKCUw4MC44LDEyLjZ6Ii8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik02NS40LDE4LjljMC44LDIsMS4zLDQuMSwxLjMsNi40YzAsMi4zLTAuNSw0LjQtMS4zLDYuNGw2LjIsMi42YzEuMS0yLjgsMS44LTUuOCwxLjgtOC45cy0wLjYtNi4yLTEuOC04LjkKCUw2NS40LDE4Ljl6Ii8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0zNC42LDMxLjdjLTAuOC0yLTEuMy00LjEtMS4zLTYuNGMwLTIuMywwLjUtNC40LDEuMy02LjRsLTYuMi0yLjZjLTEuMSwyLjgtMS44LDUuOC0xLjgsOC45czAuNiw2LjIsMS44LDguOQoJTDM0LjYsMzEuN3oiLz4KPHBhdGggY2xhc3M9InN0MiIgZD0iTTgzLjMsNTBoLTMwVjM0LjdjMy45LTEuNCw2LjctNSw2LjctOS40YzAtNS41LTQuNS0xMC0xMC0xMGMtNS41LDAtMTAsNC41LTEwLDEwYzAsNC40LDIuOCw4LDYuNyw5LjRWNTBoLTMwCglDMTMsNTAsMTAsNTMsMTAsNTYuN3YyMGMwLDMuNywzLDYuNyw2LjcsNi43VjkwaDYuN3YtNi43aDIwdi0yMEg1MHYyMGg2Ljd2LTIwaDYuN3YyMEg3MHYtMjBoNi43djIwVjkwaDYuN3YtNi43CgljMy43LDAsNi43LTMsNi43LTYuN3YtMjBDOTAsNTMsODcsNTAsODMuMyw1MHogTTM2LjcsNzBIMjMuM3YtNi43aDEzLjNWNzB6Ii8+Cjwvc3ZnPgo="
+ },
+ "outlet": {
+ "index": 9,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik03Ny42LDIwbC01NS4yLDBjLTE2LjYsMTYuNi0xNi42LDQzLjQsMCw2MGw1NS4yLDBDOTQuMSw2My40LDk0LjEsMzYuNiw3Ny42LDIweiBNMzYuNyw1My4zSDMwdi0yMGg2LjdWNTMuM3oKCSBNNTYuNyw2Ni43SDQzLjNWNjBoMTMuM1Y2Ni43eiBNNzAsNTMuM2gtNi43di0yMEg3MFY1My4zeiIvPgo8L3N2Zz4K"
+ },
+ "settings": {
+ "index": 10,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04OS44LDUzLjNjMC4xLTEuMSwwLjItMi4yLDAuMi0zLjNjMC0xLjEtMC4xLTIuMi0wLjItMy4zaC02LjdjLTAuMi0yLjItMC42LTQuMy0xLjItNi4zbDYuMi0yLjYKCWMtMC43LTIuMS0xLjYtNC4yLTIuNi02LjFsLTYuMiwyLjZjLTEtMS45LTIuMi0zLjctMy42LTUuM2w0LjctNC43Yy0xLjQtMS43LTMtMy4zLTQuNy00LjdsLTQuNyw0LjdjLTEuNi0xLjQtMy40LTIuNS01LjMtMy42CglsMi42LTYuMmMtMi0xLTQtMS45LTYuMS0yLjZsLTIuNiw2LjJjLTItMC42LTQuMS0xLTYuMy0xLjJ2LTYuN0M1Mi4yLDEwLjEsNTEuMSwxMCw1MCwxMGMtMS4xLDAtMi4yLDAuMS0zLjMsMC4ydjYuNwoJYy0yLjIsMC4yLTQuMywwLjYtNi4zLDEuMmwtMi42LTYuMmMtMi4xLDAuNy00LjIsMS42LTYuMSwyLjZsMi42LDYuMmMtMS45LDEtMy43LDIuMi01LjMsMy42bC00LjctNC43Yy0xLjcsMS40LTMuMywzLTQuNyw0LjcKCWw0LjcsNC43Yy0xLjMsMS43LTIuNSwzLjQtMy42LDUuM2wtNi4yLTIuNmMtMSwyLTEuOSw0LTIuNiw2LjFsNi4yLDIuNmMtMC42LDItMSw0LjEtMS4yLDYuM2gtNi43QzEwLjEsNDcuOCwxMCw0OC45LDEwLDUwCgljMCwxLjEsMC4xLDIuMiwwLjIsMy4zaDYuN2MwLjIsMi4yLDAuNiw0LjMsMS4yLDYuM2wtNi4yLDIuNmMwLjcsMi4xLDEuNiw0LjIsMi42LDYuMWw2LjItMi42aDBjMSwxLjksMi4yLDMuNywzLjYsNS4zbDAsMAoJbC00LjcsNC43YzEuNCwxLjcsMywzLjMsNC43LDQuN2w0LjctNC43YzEuNiwxLjQsMy40LDIuNSw1LjMsMy42djBsLTIuNiw2LjJjMiwxLDQsMS45LDYuMSwyLjZsMi42LTYuMnYwYzIsMC42LDQuMSwxLDYuMywxLjN2Ni43CgljMS4xLDAuMSwyLjIsMC4yLDMuMywwLjJjMS4xLDAsMi4yLTAuMSwzLjMtMC4ydi02LjdjMi4yLTAuMiw0LjMtMC42LDYuMy0xLjNsMi42LDYuMmMyLjEtMC43LDQuMi0xLjYsNi4yLTIuNmwtMi42LTYuMgoJYzEuOS0xLDMuNy0yLjIsNS4zLTMuNmw0LjcsNC43YzEuNy0xLjQsMy4zLTMsNC43LTQuN2wtNC43LTQuN2MxLjQtMS42LDIuNS0zLjQsMy42LTUuM2gwbDYuMiwyLjZjMS0yLDEuOS00LDIuNi02LjFsLTYuMi0yLjYKCWMwLjYtMiwxLTQuMSwxLjMtNi4zTDg5LjgsNTMuM0w4OS44LDUzLjN6IE0zMCw1MGMwLTEwLjYsOC4zLTE5LjMsMTguNy0xOS45TDQwLDUzLjNoMTAuNGwtNS45LDE1LjlDMzYuMSw2Ni44LDMwLDU5LjEsMzAsNTB6CgkgTTUxLjMsNjkuOUw2MCw0Ni43SDQ5LjZsNi0xNS45QzYzLjksMzMuMiw3MCw0MC45LDcwLDUwQzcwLDYwLjYsNjEuNyw2OS4zLDUxLjMsNjkuOXoiLz4KPC9zdmc+Cg=="
+ },
+ "socket": {
+ "index": 11,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+Cjxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iNTguMywyNi43IDU4LjMsMTAgNTEuNywxMCA1MS43LDI2LjcgMzUsMjYuNyAzNSwxMCAyOC4zLDEwIDI4LjMsMjYuNyAxNi43LDI2LjcgMTYuNyw0MCA3MCw0MCAKCTcwLDI2LjcgIi8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik01My4zLDgzLjNjLTMuNywwLTYuNy0zLTYuNy02LjdoMTBsNi43LTMwaC00MGw2LjcsMzBoMTBDNDAsODQsNDYsOTAsNTMuMyw5MGgzMHYtNi43SDUzLjN6Ii8+Cjwvc3ZnPgo="
+ },
+ "toaster_oven": {
+ "index": 12,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qye2ZpbGw6I0I3QjdCNzt9Cgkuc3Qze2ZpbGw6IzcwNzA3MDt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0yMy4zLDQwVjI2LjdjMC0xLjgsMS41LTMuMywzLjMtMy4zaDQwYzEuOCwwLDMuMywxLjUsMy4zLDMuM1Y0MEgyMy4zeiIvPgo8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODMuMyw1NXYtMy4zYzAtMy43LTMtNi43LTYuNy02LjdoLTYwQzEzLDQ1LDEwLDQ4LDEwLDUxLjd2MzEuN2g2LjdWOTBoNi43di02LjdINzBWOTBoNi43di02LjdoNi43di0yMEw5MCw2MAoJdi01SDgzLjN6IE01Ni43LDczLjNjLTEuOCwwLTMuMy0xLjUtMy4zLTMuM2MwLTEuOCwxLjUtMy4zLDMuMy0zLjNjMS44LDAsMy4zLDEuNSwzLjMsMy4zQzYwLDcxLjgsNTguNSw3My4zLDU2LjcsNzMuM3ogTTcwLDczLjMKCWMtMS44LDAtMy4zLTEuNS0zLjMtMy4zYzAtMS44LDEuNS0zLjMsMy4zLTMuM3MzLjMsMS41LDMuMywzLjNDNzMuMyw3MS44LDcxLjgsNzMuMyw3MCw3My4zeiIvPgo8cmVjdCB4PSI0My4zIiB5PSIxMCIgY2xhc3M9InN0MiIgd2lkdGg9IjYuNyIgaGVpZ2h0PSI4LjMiLz4KPHJlY3QgeD0iMzAiIHk9IjEwIiBjbGFzcz0ic3QzIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjguMyIvPgo8cmVjdCB4PSI1Ni43IiB5PSIxMCIgY2xhc3M9InN0MyIgd2lkdGg9IjYuNyIgaGVpZ2h0PSI4LjMiLz4KPC9zdmc+Cg=="
+ },
+ "trash": {
+ "index": 13,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzcwNzA3MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qye2ZpbGw6I0I3QjdCNzt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik05MCwzNi43VjMwYzAtMy43LTMtNi43LTYuNy02LjdIMTYuN2MtMy43LDAtNi43LDMtNi43LDYuN3Y2LjdIOTB6Ii8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0xNi43LDQzLjN2NDBjMCwzLjcsMyw2LjcsNi43LDYuN2g1My4zYzMuNywwLDYuNy0zLDYuNy02Ljd2LTQwSDE2Ljd6IE0zNi43LDgwSDMwVjUzLjNoNi43VjgweiBNNTMuMyw4MGgtNi43CglWNTMuM2g2LjdWODB6IE03MCw4MGgtNi43VjUzLjNINzBWODB6Ii8+CjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik02MCwxMEg0MGMtMy43LDAtNi43LDMtNi43LDYuN2gzMy4zQzY2LjcsMTMsNjMuNywxMCw2MCwxMHoiLz4KPC9zdmc+Cg=="
+ },
+ "tv": {
+ "index": 14,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6Izc3RDM4RTt9Cgkuc3Qxe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qye2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik03MCw3OC4zVjkwaDEzLjNjMy43LDAsNi43LTMsNi43LTYuN3YtNUg3MHoiLz4KPHBhdGggY2xhc3M9InN0MSIgZD0iTTgzLjMsMzYuN0g3MHYzNi43aDIwdi0zMEM5MCwzOS43LDg3LDM2LjcsODMuMywzNi43eiBNODAsNjMuM2MtMS44LDAtMy4zLTEuNS0zLjMtMy4zczEuNS0zLjMsMy4zLTMuMwoJYzEuOCwwLDMuMywxLjUsMy4zLDMuM1M4MS44LDYzLjMsODAsNjMuM3ogTTgwLDUzLjNjLTEuOCwwLTMuMy0xLjUtMy4zLTMuM3MxLjUtMy4zLDMuMy0zLjNjMS44LDAsMy4zLDEuNSwzLjMsMy4zCglTODEuOCw1My4zLDgwLDUzLjN6Ii8+CjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik02OCwxNC43TDYzLjMsMTBMNDAsMzMuM0wxNi43LDEwTDEyLDE0LjdsMjEuOSwyMkgxNi43Yy0zLjcsMC02LjcsMy02LjcsNi43djQwYzAsMy43LDMsNi43LDYuNyw2LjdINjVWMzYuNwoJSDQ2LjFMNjgsMTQuN3oiLz4KPC9zdmc+Cg=="
+ },
+ "voice_assistant": {
+ "index": 15,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik01MCwxMEwyMCwyNi43djQ2LjdMNTAsOTBsMzAtMTYuN1YyNi43TDUwLDEweiBNNDYuNyw4MC41bC0yMC0xMS4xdi00LjdsMjAsMTEuMVY4MC41eiBNNDYuNyw2OC4ybC0yMC0xMS4xCgl2LTcuNmwyMCwxMS4xVjY4LjJ6IE00Ni43LDUyLjlsLTIwLTExLjF2LTcuNmwyMCwxMS4xVjUyLjl6IE01MCwzNC44Yy01LjUsMC0xMC0yLjgtMTAtNi4yYzAtMy41LDQuNS02LjIsMTAtNi4yCgljNS41LDAsMTAsMi44LDEwLDYuMkM2MCwzMiw1NS41LDM0LjgsNTAsMzQuOHogTTY2LjcsNTMuMUw2MCw1Ni44di03LjZsNi43LTMuN1Y1My4xeiIvPgo8L3N2Zz4K"
+ },
+ "washer": {
+ "index": 16,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qye2ZpbGw6IzcwNzA3MDt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xMCwzMHY1My4zYzAsMy43LDMsNi43LDYuNyw2LjdoNjYuN2MzLjcsMCw2LjctMyw2LjctNi43VjMwSDEweiBNNTAsODMuM2MtMTIuOSwwLTIzLjMtMTAuNC0yMy4zLTIzLjMKCVMzNy4xLDM2LjcsNTAsMzYuN2MxMi45LDAsMjMuMywxMC40LDIzLjMsMjMuM1M2Mi45LDgzLjMsNTAsODMuM3oiLz4KPHBhdGggY2xhc3M9InN0MSIgZD0iTTUwLDQzLjNjLTkuMiwwLTE2LjcsNy41LTE2LjcsMTYuN2MwLDkuMiw3LjUsMTYuNywxNi43LDE2LjdjOS4yLDAsMTYuNy03LjUsMTYuNy0xNi43CglDNjYuNyw1MC44LDU5LjIsNDMuMyw1MCw0My4zeiBNNTAsNzBjLTUuNSwwLTEwLTQuNS0xMC0xMGMwLTUuNSw0LjUtMTAsMTAtMTBzMTAsNC41LDEwLDEwQzYwLDY1LjUsNTUuNSw3MCw1MCw3MHoiLz4KPHBhdGggY2xhc3M9InN0MiIgZD0iTTgzLjMsMTBIMTYuN0MxMywxMCwxMCwxMywxMCwxNi43djYuN2gxMHYtNi43aDIwdjYuN2gyMHYtNi43aDYuN3Y2LjdoNi43di02LjdIODB2Ni43aDEwdi02LjcKCUM5MCwxMyw4NywxMCw4My4zLDEweiIvPgo8L3N2Zz4K"
+ },
+ "water_heater": {
+ "index": 17,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzcwNzA3MDt9Cgkuc3Qxe2ZpbGw6I0I3QjdCNzt9Cgkuc3Qye2ZpbGw6Izc3RDM4RTt9Cgkuc3Qze2ZpbGw6IzQ2ODg2MDt9Cgkuc3Q0e2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxyZWN0IHg9IjI2LjciIHk9IjcwIiBjbGFzcz0ic3QwIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjIwIi8+CjxyZWN0IHg9IjI2LjciIHk9IjUwIiBjbGFzcz0ic3QxIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjEzLjMiLz4KPHJlY3QgeD0iNDAiIHk9IjUwIiBjbGFzcz0ic3QyIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjIwIi8+CjxyZWN0IHg9IjQwIiB5PSI3Ni43IiBjbGFzcz0ic3QxIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjYuNyIvPgo8cmVjdCB4PSI1My4zIiB5PSI1MCIgY2xhc3M9InN0MCIgd2lkdGg9IjYuNyIgaGVpZ2h0PSI2LjciLz4KPHJlY3QgeD0iNTMuMyIgeT0iNjMuMyIgY2xhc3M9InN0MSIgd2lkdGg9IjYuNyIgaGVpZ2h0PSIxMy4zIi8+CjxyZWN0IHg9IjUzLjMiIHk9IjgzLjMiIGNsYXNzPSJzdDIiIHdpZHRoPSI2LjciIGhlaWdodD0iNi43Ii8+CjxyZWN0IHg9IjY2LjciIHk9IjUwIiBjbGFzcz0ic3QzIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjEzLjMiLz4KPHJlY3QgeD0iNjYuNyIgeT0iNzAiIGNsYXNzPSJzdDEiIHdpZHRoPSI2LjciIGhlaWdodD0iMjAiLz4KPHBhdGggY2xhc3M9InN0NCIgZD0iTTUzLjMsMjAuM1YxMGgtNi43djEwLjNjLTExLjMsMS42LTIwLDExLjMtMjAsMjMuMWg0Ni43QzczLjMsMzEuNiw2NC42LDIxLjksNTMuMywyMC4zeiIvPgo8L3N2Zz4K"
+ }
+ },
+ "type": "value"
+ }
+ ]
+ },
+ {
+ "id": "custom.align",
+ "value": "center"
+ },
+ {
+ "id": "custom.width",
+ "value": 55
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Last State Time"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "dateTimeAsUS"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "KWH"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "kwatth"
+ },
+ {
+ "id": "decimals",
+ "value": 2
+ },
+ {
+ "id": "custom.width",
+ "value": 70
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Watts (Current)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "watt"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Last State"
+ },
+ "properties": [
+ {
+ "id": "custom.displayMode",
+ "value": "color-background"
+ },
+ {
+ "id": "mappings",
+ "value": [
+ {
+ "options": {
+ "Idle": {
+ "color": "light-blue",
+ "index": 2
+ },
+ "Off": {
+ "color": "dark-blue",
+ "index": 1
+ },
+ "On": {
+ "color": "dark-red",
+ "index": 0
+ }
+ },
+ "type": "value"
+ },
+ {
+ "options": {
+ "match": "null+nan",
+ "result": {
+ "color": "transparent",
+ "index": 3
+ }
+ },
+ "type": "special"
+ }
+ ]
+ },
+ {
+ "id": "custom.align",
+ "value": "center"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Runs"
+ },
+ "properties": [
+ {
+ "id": "custom.width",
+ "value": 66
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 12,
+ "w": 4,
+ "x": 13,
+ "y": 16
+ },
+ "hideTimeOverride": true,
+ "id": 13,
+ "maxDataPoints": 1,
+ "options": {
+ "showHeader": true,
+ "sortBy": [
+ {
+ "desc": true,
+ "displayName": "Runs"
+ }
+ ]
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "query": "SELECT last(*) FROM \"sense_devices\" WHERE (\"name\" =~ /^$devices$/) and $timeFilter GROUP BY time(1d), \"name\" fill(null)",
+ "rawQuery": true,
+ "refId": "A",
+ "resultFormat": "table"
+ }
+ ],
+ "timeFrom": "1m",
+ "title": "Device Details - Current Month",
+ "transformations": [
+ {
+ "id": "organize",
+ "options": {
+ "excludeByName": {
+ "Time": true,
+ "last_ao_w": true,
+ "last_avg_duration": true,
+ "last_avg_monthly_KWH": true,
+ "last_avg_monthly_cost": true,
+ "last_avg_monthly_pct": true,
+ "last_avg_monthly_runs": true,
+ "last_avg_watts": true,
+ "last_current_ao_wattage": true,
+ "last_current_month_KWH": true,
+ "last_current_month_cost": true,
+ "last_current_month_runs": false,
+ "last_current_watts": true,
+ "last_e": true,
+ "last_i": true,
+ "last_icon": false,
+ "last_last_state": true,
+ "last_last_state_time_epoch": true,
+ "last_v": true,
+ "last_watts": false,
+ "last_yearly_KWH": true,
+ "last_yearly_cost": true,
+ "last_yearly_text": true
+ },
+ "indexByName": {
+ "Time": 0,
+ "last_avg_duration": 4,
+ "last_avg_monthly_KWH": 5,
+ "last_avg_monthly_cost": 6,
+ "last_avg_monthly_pct": 7,
+ "last_avg_monthly_runs": 8,
+ "last_avg_watts": 9,
+ "last_current_ao_wattage": 10,
+ "last_current_month_KWH": 11,
+ "last_current_month_cost": 12,
+ "last_current_month_runs": 13,
+ "last_current_watts": 3,
+ "last_icon": 2,
+ "last_last_state": 14,
+ "last_last_state_time_epoch": 15,
+ "last_yearly_KWH": 16,
+ "last_yearly_cost": 17,
+ "last_yearly_text": 18,
+ "name": 1
+ },
+ "renameByName": {
+ "last_ao_w": "Watts (AO)",
+ "last_avg_duration": "Duration (Average)",
+ "last_avg_monthly_KWH": "Monthly KWH (Average)",
+ "last_avg_monthly_cost": "Monthly Cost (Average)",
+ "last_avg_monthly_pct": "Monthly Percent (Average)",
+ "last_avg_monthly_runs": "Monthly Runs (Average)",
+ "last_avg_watts": "Watts (Average)",
+ "last_current_ao_wattage": "Always On Wattage (Average)",
+ "last_current_month_KWH": "KWH",
+ "last_current_month_cost": "Cost",
+ "last_current_month_runs": "Runs",
+ "last_current_watts": "Watts (Current)",
+ "last_e": "Who Knows",
+ "last_i": "Amps",
+ "last_icon": "Icon",
+ "last_last_state": "Last State",
+ "last_last_state_time_epoch": "Last State Time",
+ "last_v": "Volts",
+ "last_watts": "Watts",
+ "last_yearly_KWH": "KWH (Yearly)",
+ "last_yearly_cost": "Cost (Yearly)",
+ "last_yearly_text": "Text (Yearly)",
+ "name": "Name"
+ }
+ }
+ }
+ ],
+ "type": "table"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "align": "auto",
+ "displayMode": "auto",
+ "filterable": false
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Duration (Average)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "s"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Monthly Percent (Average)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "percent"
+ },
+ {
+ "id": "decimals",
+ "value": 1
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Watts (Average)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "watt"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "kWh"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "kwatth"
+ },
+ {
+ "id": "decimals",
+ "value": 2
+ },
+ {
+ "id": "custom.width",
+ "value": 94
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Cost (Current Month)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "currencyUSD"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Cost"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "currencyUSD"
+ },
+ {
+ "id": "decimals",
+ "value": 2
+ },
+ {
+ "id": "custom.width",
+ "value": 76
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Icon"
+ },
+ "properties": [
+ {
+ "id": "custom.displayMode",
+ "value": "image"
+ },
+ {
+ "id": "mappings",
+ "value": [
+ {
+ "options": {
+ "ac": {
+ "index": 0,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04NC43LDUzLjdsLTE0LjgsNGwtNC4yLTIuNGMwLjYtMS43LDAuOS0zLjQsMC45LTUuMnMtMC40LTMuNi0wLjktNS4ybDQuMi0yLjRsMTQuOCw0bDEuNy02LjRsLTguMy0yLjIKCWw4LjItNC43TDgzLDI3LjFsLTguMiw0LjdsMi4yLTguM2wtNi40LTEuN2wtNCwxNC44TDYyLjQsMzljLTIuMy0yLjYtNS41LTQuNi05LjEtNS4zdi00LjhMNjQuMSwxOGwtNC43LTQuN2wtNi4xLDYuMVYxMGgtNi43djkuNAoJbC02LjEtNi4xTDM1LjksMThsMTAuOCwxMC44djQuOGMtMy42LDAuNy02LjcsMi43LTkuMSw1LjNsLTQuMi0yLjRsLTQtMTQuOEwyMywyMy41bDIuMiw4LjNMMTcsMjcuMWwtMy4zLDUuOGw4LjIsNC43bC04LjMsMi4yCglsMS43LDYuNGwxNC44LTRsNC4yLDIuNGMtMC42LDEuNy0wLjksMy40LTAuOSw1LjJzMC40LDMuNiwwLjksNS4yTDMwLDU3LjdsLTE0LjgtNGwtMS43LDYuNGw4LjMsMi4ybC04LjIsNC43bDMuMyw1LjhsOC4yLTQuNwoJTDIzLDc2LjVsNi40LDEuN2w0LTE0LjhsNC4yLTIuNGMyLjMsMi42LDUuNSw0LjYsOS4xLDUuM3Y0LjhMMzUuOSw4Mmw0LjcsNC43bDYuMS02LjFWOTBoNi43di05LjRsNi4xLDYuMWw0LjctNC43TDUzLjMsNzEuMXYtNC44CgljMy42LTAuNyw2LjctMi43LDkuMS01LjNsNC4yLDIuNGw0LDE0LjhsNi40LTEuN2wtMi4yLTguM2w4LjIsNC43bDMuMy01LjhsLTguMi00LjdsOC4zLTIuMkw4NC43LDUzLjd6Ii8+Cjwvc3ZnPgo="
+ },
+ "always_on": {
+ "index": 1,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04My4zLDEwSDE2LjdDMTMsMTAsMTAsMTMsMTAsMTYuN3Y2Ni43YzAsMy43LDMsNi43LDYuNyw2LjdoNjYuN2MzLjcsMCw2LjctMyw2LjctNi43VjE2LjcKCUM5MCwxMyw4NywxMCw4My4zLDEweiBNNzguMyw4MGgtNi43di05LjJDNjYuMiw3Ni40LDU4LjUsODAsNTAsODBjLTE2LjYsMC0zMC0xMy40LTMwLTMwaDYuN2MwLDEyLjksMTAuNCwyMy4zLDIzLjMsMjMuMwoJYzYuNiwwLDEyLjYtMi44LDE2LjktNy4yaC05LjF2LTYuN2wxNy4yLDB2MGMxLjgsMCwzLjMsMS41LDMuMywzLjNWODB6IE03My4zLDUwYzAtMTIuOS0xMC40LTIzLjMtMjMuMy0yMy4zCgljLTYuNiwwLTEyLjYsMi44LTE2LjksNy4yaDkuMnY2LjdsLTE3LjIsMGwwLDBjLTEuOCwwLTMuMy0xLjUtMy4zLTMuM1YyMGg2LjdsMCw5LjJDMzMuOCwyMy42LDQxLjUsMjAsNTAsMjBjMTYuNiwwLDMwLDEzLjQsMzAsMzAKCUg3My4zeiIvPgo8L3N2Zz4K"
+ },
+ "car": {
+ "index": 2,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qxe2ZpbGw6IzQ2ODg2MDt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04My4zLDU2LjdsLTUuNi0xNi44Yy0wLjYtMS43LTIuNi0zLjItNC40LTMuMkg0Ni43bC0xMy4zLDIwbC0yMC4zLDguN0MxMS40LDY2LjEsMTAsNjguMiwxMCw3MHYxMy4zaDEwLjYKCWMxLjQsMy45LDUsNi43LDkuNCw2LjdzOC0yLjgsOS40LTYuN2gyNC42YzEuNCwzLjksNSw2LjcsOS40LDYuN2M0LjMsMCw4LTIuOCw5LjQtNi43SDkwdi0yMEM5MCw1OS43LDg3LDU2LjcsODMuMyw1Ni43eiBNMzAsODMuMwoJYy0xLjgsMC0zLjMtMS41LTMuMy0zLjNzMS41LTMuMywzLjMtMy4zczMuMywxLjUsMy4zLDMuM1MzMS44LDgzLjMsMzAsODMuM3ogTTU2LjcsNTYuN0g0MS4zbDguOS0xMy4zaDYuNEw1Ni43LDU2LjdMNTYuNyw1Ni43egoJIE02My4zLDU2LjdWNDMuM2g4LjVsNC40LDEzLjNINjMuM3ogTTczLjMsODMuM2MtMS44LDAtMy4zLTEuNS0zLjMtMy4zczEuNS0zLjMsMy4zLTMuM2MxLjgsMCwzLjMsMS41LDMuMywzLjNTNzUuMiw4My4zLDczLjMsODMuMwoJeiIvPgo8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODMuMywxMHY4LjNjMCwxLjgtMS41LDMuMy0zLjMsMy4zSDM2Ljd2LTVjMC0zLjctMy02LjctNi43LTYuN0gyMHY1LjhoLTYuN2MtMS44LDAtMy4zLDEuNS0zLjMsMy4zCgljMCwxLjgsMS41LDMuMywzLjMsMy4zSDIwdjVoLTYuN2MtMS44LDAtMy4zLDEuNS0zLjMsMy4zYzAsMS44LDEuNSwzLjMsMy4zLDMuM0gyMFY0MGgxMGMzLjcsMCw2LjctMyw2LjctNi43di01SDgwCgljNS41LDAsMTAtNC41LDEwLTEwVjEwSDgzLjN6Ii8+Cjwvc3ZnPgo="
+ },
+ "computer": {
+ "index": 3,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0yMy4zLDgzLjNWOTBoMjBWMjMuM0gxNi43Yy0zLjcsMC02LjcsMy02LjcsNi43djQwYzAsMy43LDMsNi43LDYuNyw2LjdoMjB2Ni43SDIzLjN6Ii8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik04My4zLDEwSDU2LjdDNTMsMTAsNTAsMTMsNTAsMTYuN3Y2Ni43YzAsMy43LDMsNi43LDYuNyw2LjdoMjYuN2MzLjcsMCw2LjctMyw2LjctNi43VjE2LjcKCUM5MCwxMyw4NywxMCw4My4zLDEweiBNNzAsODMuM2MtMS44LDAtMy4zLTEuNS0zLjMtMy4zczEuNS0zLjMsMy4zLTMuM3MzLjMsMS41LDMuMywzLjNTNzEuOCw4My4zLDcwLDgzLjN6IE04My4zLDQzLjNINTYuN3YtNi43CgloMjYuN1Y0My4zeiBNODMuMywzMEg1Ni43di02LjdoMjYuN1YzMHoiLz4KPC9zdmc+Cg=="
+ },
+ "dishes": {
+ "index": 4,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qye2ZpbGw6IzcwNzA3MDt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xMCwzMHY1My4zYzAsMy43LDMsNi43LDYuNyw2LjdoNjYuN2MzLjcsMCw2LjctMyw2LjctNi43VjMwSDEweiBNNTAsODMuM2MtMTIuOSwwLTIzLjMtMTAuNC0yMy4zLTIzLjMKCVMzNy4xLDM2LjcsNTAsMzYuN2MxMi45LDAsMjMuMywxMC40LDIzLjMsMjMuM1M2Mi45LDgzLjMsNTAsODMuM3oiLz4KPHBhdGggY2xhc3M9InN0MSIgZD0iTTUwLDQzLjNjLTkuMiwwLTE2LjcsNy41LTE2LjcsMTYuN2MwLDkuMiw3LjUsMTYuNywxNi43LDE2LjdjOS4yLDAsMTYuNy03LjUsMTYuNy0xNi43CglDNjYuNyw1MC44LDU5LjIsNDMuMyw1MCw0My4zeiBNNTAsNzBjLTUuNSwwLTEwLTQuNS0xMC0xMGMwLTUuNSw0LjUtMTAsMTAtMTBzMTAsNC41LDEwLDEwQzYwLDY1LjUsNTUuNSw3MCw1MCw3MHoiLz4KPHBhdGggY2xhc3M9InN0MiIgZD0iTTgzLjMsMTBIMTYuN0MxMywxMCwxMCwxMywxMCwxNi43djYuN2gxMHYtNi43aDIwdjYuN2gyMHYtNi43aDYuN3Y2LjdoNi43di02LjdIODB2Ni43aDEwdi02LjcKCUM5MCwxMyw4NywxMCw4My4zLDEweiIvPgo8L3N2Zz4K"
+ },
+ "fridge": {
+ "index": 5,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik03My4zLDQzLjN2MjBoLTYuN3YtMjBIMjBWODBjMCw1LjUsNC41LDEwLDEwLDEwaDQwYzUuNSwwLDEwLTQuNSwxMC0xMFY0My4zSDczLjN6Ii8+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik02Ni43LDM2LjdWMjMuM2g2Ljd2MTMuM0g4MFYyMGMwLTUuNS00LjUtMTAtMTAtMTBIMzBjLTUuNSwwLTEwLDQuNS0xMCwxMHYxNi43SDY2Ljd6Ii8+Cjwvc3ZnPgo="
+ },
+ "garage": {
+ "index": 6,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qxe2ZpbGw6IzcwNzA3MDt9Cgkuc3Qye2ZpbGw6Izc3RDM4RTt9Cjwvc3R5bGU+Cjxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iMTAsMzYuNyAxMCw5MCAyMCw5MCAyMCw0Ni43IDgwLDQ2LjcgODAsOTAgOTAsOTAgOTAsMzYuNyAiLz4KPHBvbHlnb24gY2xhc3M9InN0MSIgcG9pbnRzPSIyNi43LDUzLjMgMjYuNyw3MCA0Ni43LDcwIDQ2LjcsNjMuMyA1My4zLDYzLjMgNTMuMyw3MCA3My4zLDcwIDczLjMsNTMuMyAiLz4KPHBvbHlnb24gY2xhc3M9InN0MiIgcG9pbnRzPSI1MCwxMCAxMCwzMCA5MCwzMCAiLz4KPC9zdmc+Cg=="
+ },
+ "heat": {
+ "index": 7,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6Izc3RDM4RTt9Cgkuc3Qye2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik01MCw3NWMtMTIsMC0yNC4yLTIuNS0zMS41LTcuNEMyMy4xLDgwLjYsMzUuNCw5MCw1MCw5MHMyNi45LTkuNCwzMS41LTIyLjRDNzQuMiw3Mi41LDYyLDc1LDUwLDc1eiIvPgo8cGF0aCBjbGFzcz0ic3QxIiBkPSJNNzYuMSw0OC40Yy0xLDQuNC0zLjIsOC41LTYuNCwxMS44bC0xLjUsMS41bC0zNi40LDBsLTEuNS0xLjVjLTMuMy0zLjMtNS41LTcuNC02LjQtMTEuOAoJYy00LjUsMi4zLTcuMiw1LjEtNy4yLDguM0MxNi43LDY0LDMxLjYsNzAsNTAsNzBzMzMuMy02LDMzLjMtMTMuM0M4My4zLDUzLjUsODAuNiw1MC43LDc2LjEsNDguNHoiLz4KPHBhdGggY2xhc3M9InN0MiIgZD0iTTMzLjksNTYuN2MtNy40LTcuNC03LjQtMTkuMywwLTI2LjdjMCw1LjUsNC41LDEwLDEwLDEwdi01LjZjMC05LjUsMy45LTE4LjEsMTAuMS0yNC40YzAsNC44LDEuOCw5LjYsNS41LDEzLjMKCWw2LjcsNi43YzcuNCw3LjQsNy40LDE5LjMsMCwyNi43TDMzLjksNTYuN3oiLz4KPC9zdmc+Cg=="
+ },
+ "modem": {
+ "index": 8,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6Izc3RDM4RTt9Cgkuc3Qye2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xMyw0MC42Yy0yLTQuNy0zLTkuOS0zLTE1LjNjMC01LjQsMS4xLTEwLjYsMy0xNS4zbDYuMiwyLjZjLTEuNiwzLjktMi41LDguMi0yLjUsMTIuOGMwLDQuNSwwLjksOC44LDIuNSwxMi44CglMMTMsNDAuNnoiLz4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTgwLjgsMTIuNmMxLjYsMy45LDIuNSw4LjIsMi41LDEyLjhjMCw0LjUtMC45LDguOC0yLjUsMTIuOGw2LjIsMi42YzItNC43LDMtOS45LDMtMTUuM2MwLTUuNC0xLjEtMTAuNi0zLTE1LjMKCUw4MC44LDEyLjZ6Ii8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik02NS40LDE4LjljMC44LDIsMS4zLDQuMSwxLjMsNi40YzAsMi4zLTAuNSw0LjQtMS4zLDYuNGw2LjIsMi42YzEuMS0yLjgsMS44LTUuOCwxLjgtOC45cy0wLjYtNi4yLTEuOC04LjkKCUw2NS40LDE4Ljl6Ii8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0zNC42LDMxLjdjLTAuOC0yLTEuMy00LjEtMS4zLTYuNGMwLTIuMywwLjUtNC40LDEuMy02LjRsLTYuMi0yLjZjLTEuMSwyLjgtMS44LDUuOC0xLjgsOC45czAuNiw2LjIsMS44LDguOQoJTDM0LjYsMzEuN3oiLz4KPHBhdGggY2xhc3M9InN0MiIgZD0iTTgzLjMsNTBoLTMwVjM0LjdjMy45LTEuNCw2LjctNSw2LjctOS40YzAtNS41LTQuNS0xMC0xMC0xMGMtNS41LDAtMTAsNC41LTEwLDEwYzAsNC40LDIuOCw4LDYuNyw5LjRWNTBoLTMwCglDMTMsNTAsMTAsNTMsMTAsNTYuN3YyMGMwLDMuNywzLDYuNyw2LjcsNi43VjkwaDYuN3YtNi43aDIwdi0yMEg1MHYyMGg2Ljd2LTIwaDYuN3YyMEg3MHYtMjBoNi43djIwVjkwaDYuN3YtNi43CgljMy43LDAsNi43LTMsNi43LTYuN3YtMjBDOTAsNTMsODcsNTAsODMuMyw1MHogTTM2LjcsNzBIMjMuM3YtNi43aDEzLjNWNzB6Ii8+Cjwvc3ZnPgo="
+ },
+ "outlet": {
+ "index": 9,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik03Ny42LDIwbC01NS4yLDBjLTE2LjYsMTYuNi0xNi42LDQzLjQsMCw2MGw1NS4yLDBDOTQuMSw2My40LDk0LjEsMzYuNiw3Ny42LDIweiBNMzYuNyw1My4zSDMwdi0yMGg2LjdWNTMuM3oKCSBNNTYuNyw2Ni43SDQzLjNWNjBoMTMuM1Y2Ni43eiBNNzAsNTMuM2gtNi43di0yMEg3MFY1My4zeiIvPgo8L3N2Zz4K"
+ },
+ "settings": {
+ "index": 10,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04OS44LDUzLjNjMC4xLTEuMSwwLjItMi4yLDAuMi0zLjNjMC0xLjEtMC4xLTIuMi0wLjItMy4zaC02LjdjLTAuMi0yLjItMC42LTQuMy0xLjItNi4zbDYuMi0yLjYKCWMtMC43LTIuMS0xLjYtNC4yLTIuNi02LjFsLTYuMiwyLjZjLTEtMS45LTIuMi0zLjctMy42LTUuM2w0LjctNC43Yy0xLjQtMS43LTMtMy4zLTQuNy00LjdsLTQuNyw0LjdjLTEuNi0xLjQtMy40LTIuNS01LjMtMy42CglsMi42LTYuMmMtMi0xLTQtMS45LTYuMS0yLjZsLTIuNiw2LjJjLTItMC42LTQuMS0xLTYuMy0xLjJ2LTYuN0M1Mi4yLDEwLjEsNTEuMSwxMCw1MCwxMGMtMS4xLDAtMi4yLDAuMS0zLjMsMC4ydjYuNwoJYy0yLjIsMC4yLTQuMywwLjYtNi4zLDEuMmwtMi42LTYuMmMtMi4xLDAuNy00LjIsMS42LTYuMSwyLjZsMi42LDYuMmMtMS45LDEtMy43LDIuMi01LjMsMy42bC00LjctNC43Yy0xLjcsMS40LTMuMywzLTQuNyw0LjcKCWw0LjcsNC43Yy0xLjMsMS43LTIuNSwzLjQtMy42LDUuM2wtNi4yLTIuNmMtMSwyLTEuOSw0LTIuNiw2LjFsNi4yLDIuNmMtMC42LDItMSw0LjEtMS4yLDYuM2gtNi43QzEwLjEsNDcuOCwxMCw0OC45LDEwLDUwCgljMCwxLjEsMC4xLDIuMiwwLjIsMy4zaDYuN2MwLjIsMi4yLDAuNiw0LjMsMS4yLDYuM2wtNi4yLDIuNmMwLjcsMi4xLDEuNiw0LjIsMi42LDYuMWw2LjItMi42aDBjMSwxLjksMi4yLDMuNywzLjYsNS4zbDAsMAoJbC00LjcsNC43YzEuNCwxLjcsMywzLjMsNC43LDQuN2w0LjctNC43YzEuNiwxLjQsMy40LDIuNSw1LjMsMy42djBsLTIuNiw2LjJjMiwxLDQsMS45LDYuMSwyLjZsMi42LTYuMnYwYzIsMC42LDQuMSwxLDYuMywxLjN2Ni43CgljMS4xLDAuMSwyLjIsMC4yLDMuMywwLjJjMS4xLDAsMi4yLTAuMSwzLjMtMC4ydi02LjdjMi4yLTAuMiw0LjMtMC42LDYuMy0xLjNsMi42LDYuMmMyLjEtMC43LDQuMi0xLjYsNi4yLTIuNmwtMi42LTYuMgoJYzEuOS0xLDMuNy0yLjIsNS4zLTMuNmw0LjcsNC43YzEuNy0xLjQsMy4zLTMsNC43LTQuN2wtNC43LTQuN2MxLjQtMS42LDIuNS0zLjQsMy42LTUuM2gwbDYuMiwyLjZjMS0yLDEuOS00LDIuNi02LjFsLTYuMi0yLjYKCWMwLjYtMiwxLTQuMSwxLjMtNi4zTDg5LjgsNTMuM0w4OS44LDUzLjN6IE0zMCw1MGMwLTEwLjYsOC4zLTE5LjMsMTguNy0xOS45TDQwLDUzLjNoMTAuNGwtNS45LDE1LjlDMzYuMSw2Ni44LDMwLDU5LjEsMzAsNTB6CgkgTTUxLjMsNjkuOUw2MCw0Ni43SDQ5LjZsNi0xNS45QzYzLjksMzMuMiw3MCw0MC45LDcwLDUwQzcwLDYwLjYsNjEuNyw2OS4zLDUxLjMsNjkuOXoiLz4KPC9zdmc+Cg=="
+ },
+ "socket": {
+ "index": 11,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+Cjxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iNTguMywyNi43IDU4LjMsMTAgNTEuNywxMCA1MS43LDI2LjcgMzUsMjYuNyAzNSwxMCAyOC4zLDEwIDI4LjMsMjYuNyAxNi43LDI2LjcgMTYuNyw0MCA3MCw0MCAKCTcwLDI2LjcgIi8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik01My4zLDgzLjNjLTMuNywwLTYuNy0zLTYuNy02LjdoMTBsNi43LTMwaC00MGw2LjcsMzBoMTBDNDAsODQsNDYsOTAsNTMuMyw5MGgzMHYtNi43SDUzLjN6Ii8+Cjwvc3ZnPgo="
+ },
+ "toaster_oven": {
+ "index": 12,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qye2ZpbGw6I0I3QjdCNzt9Cgkuc3Qze2ZpbGw6IzcwNzA3MDt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0yMy4zLDQwVjI2LjdjMC0xLjgsMS41LTMuMywzLjMtMy4zaDQwYzEuOCwwLDMuMywxLjUsMy4zLDMuM1Y0MEgyMy4zeiIvPgo8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODMuMyw1NXYtMy4zYzAtMy43LTMtNi43LTYuNy02LjdoLTYwQzEzLDQ1LDEwLDQ4LDEwLDUxLjd2MzEuN2g2LjdWOTBoNi43di02LjdINzBWOTBoNi43di02LjdoNi43di0yMEw5MCw2MAoJdi01SDgzLjN6IE01Ni43LDczLjNjLTEuOCwwLTMuMy0xLjUtMy4zLTMuM2MwLTEuOCwxLjUtMy4zLDMuMy0zLjNjMS44LDAsMy4zLDEuNSwzLjMsMy4zQzYwLDcxLjgsNTguNSw3My4zLDU2LjcsNzMuM3ogTTcwLDczLjMKCWMtMS44LDAtMy4zLTEuNS0zLjMtMy4zYzAtMS44LDEuNS0zLjMsMy4zLTMuM3MzLjMsMS41LDMuMywzLjNDNzMuMyw3MS44LDcxLjgsNzMuMyw3MCw3My4zeiIvPgo8cmVjdCB4PSI0My4zIiB5PSIxMCIgY2xhc3M9InN0MiIgd2lkdGg9IjYuNyIgaGVpZ2h0PSI4LjMiLz4KPHJlY3QgeD0iMzAiIHk9IjEwIiBjbGFzcz0ic3QzIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjguMyIvPgo8cmVjdCB4PSI1Ni43IiB5PSIxMCIgY2xhc3M9InN0MyIgd2lkdGg9IjYuNyIgaGVpZ2h0PSI4LjMiLz4KPC9zdmc+Cg=="
+ },
+ "trash": {
+ "index": 13,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzcwNzA3MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qye2ZpbGw6I0I3QjdCNzt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik05MCwzNi43VjMwYzAtMy43LTMtNi43LTYuNy02LjdIMTYuN2MtMy43LDAtNi43LDMtNi43LDYuN3Y2LjdIOTB6Ii8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0xNi43LDQzLjN2NDBjMCwzLjcsMyw2LjcsNi43LDYuN2g1My4zYzMuNywwLDYuNy0zLDYuNy02Ljd2LTQwSDE2Ljd6IE0zNi43LDgwSDMwVjUzLjNoNi43VjgweiBNNTMuMyw4MGgtNi43CglWNTMuM2g2LjdWODB6IE03MCw4MGgtNi43VjUzLjNINzBWODB6Ii8+CjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik02MCwxMEg0MGMtMy43LDAtNi43LDMtNi43LDYuN2gzMy4zQzY2LjcsMTMsNjMuNywxMCw2MCwxMHoiLz4KPC9zdmc+Cg=="
+ },
+ "tv": {
+ "index": 14,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6Izc3RDM4RTt9Cgkuc3Qxe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qye2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik03MCw3OC4zVjkwaDEzLjNjMy43LDAsNi43LTMsNi43LTYuN3YtNUg3MHoiLz4KPHBhdGggY2xhc3M9InN0MSIgZD0iTTgzLjMsMzYuN0g3MHYzNi43aDIwdi0zMEM5MCwzOS43LDg3LDM2LjcsODMuMywzNi43eiBNODAsNjMuM2MtMS44LDAtMy4zLTEuNS0zLjMtMy4zczEuNS0zLjMsMy4zLTMuMwoJYzEuOCwwLDMuMywxLjUsMy4zLDMuM1M4MS44LDYzLjMsODAsNjMuM3ogTTgwLDUzLjNjLTEuOCwwLTMuMy0xLjUtMy4zLTMuM3MxLjUtMy4zLDMuMy0zLjNjMS44LDAsMy4zLDEuNSwzLjMsMy4zCglTODEuOCw1My4zLDgwLDUzLjN6Ii8+CjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik02OCwxNC43TDYzLjMsMTBMNDAsMzMuM0wxNi43LDEwTDEyLDE0LjdsMjEuOSwyMkgxNi43Yy0zLjcsMC02LjcsMy02LjcsNi43djQwYzAsMy43LDMsNi43LDYuNyw2LjdINjVWMzYuNwoJSDQ2LjFMNjgsMTQuN3oiLz4KPC9zdmc+Cg=="
+ },
+ "voice_assistant": {
+ "index": 15,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik01MCwxMEwyMCwyNi43djQ2LjdMNTAsOTBsMzAtMTYuN1YyNi43TDUwLDEweiBNNDYuNyw4MC41bC0yMC0xMS4xdi00LjdsMjAsMTEuMVY4MC41eiBNNDYuNyw2OC4ybC0yMC0xMS4xCgl2LTcuNmwyMCwxMS4xVjY4LjJ6IE00Ni43LDUyLjlsLTIwLTExLjF2LTcuNmwyMCwxMS4xVjUyLjl6IE01MCwzNC44Yy01LjUsMC0xMC0yLjgtMTAtNi4yYzAtMy41LDQuNS02LjIsMTAtNi4yCgljNS41LDAsMTAsMi44LDEwLDYuMkM2MCwzMiw1NS41LDM0LjgsNTAsMzQuOHogTTY2LjcsNTMuMUw2MCw1Ni44di03LjZsNi43LTMuN1Y1My4xeiIvPgo8L3N2Zz4K"
+ },
+ "washer": {
+ "index": 16,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ2ODg2MDt9Cgkuc3Qxe2ZpbGw6I0UzNTQzMTt9Cgkuc3Qye2ZpbGw6IzcwNzA3MDt9Cjwvc3R5bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xMCwzMHY1My4zYzAsMy43LDMsNi43LDYuNyw2LjdoNjYuN2MzLjcsMCw2LjctMyw2LjctNi43VjMwSDEweiBNNTAsODMuM2MtMTIuOSwwLTIzLjMtMTAuNC0yMy4zLTIzLjMKCVMzNy4xLDM2LjcsNTAsMzYuN2MxMi45LDAsMjMuMywxMC40LDIzLjMsMjMuM1M2Mi45LDgzLjMsNTAsODMuM3oiLz4KPHBhdGggY2xhc3M9InN0MSIgZD0iTTUwLDQzLjNjLTkuMiwwLTE2LjcsNy41LTE2LjcsMTYuN2MwLDkuMiw3LjUsMTYuNywxNi43LDE2LjdjOS4yLDAsMTYuNy03LjUsMTYuNy0xNi43CglDNjYuNyw1MC44LDU5LjIsNDMuMyw1MCw0My4zeiBNNTAsNzBjLTUuNSwwLTEwLTQuNS0xMC0xMGMwLTUuNSw0LjUtMTAsMTAtMTBzMTAsNC41LDEwLDEwQzYwLDY1LjUsNTUuNSw3MCw1MCw3MHoiLz4KPHBhdGggY2xhc3M9InN0MiIgZD0iTTgzLjMsMTBIMTYuN0MxMywxMCwxMCwxMywxMCwxNi43djYuN2gxMHYtNi43aDIwdjYuN2gyMHYtNi43aDYuN3Y2LjdoNi43di02LjdIODB2Ni43aDEwdi02LjcKCUM5MCwxMyw4NywxMCw4My4zLDEweiIvPgo8L3N2Zz4K"
+ },
+ "water_heater": {
+ "index": 17,
+ "text": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjMuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxMDAgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzcwNzA3MDt9Cgkuc3Qxe2ZpbGw6I0I3QjdCNzt9Cgkuc3Qye2ZpbGw6Izc3RDM4RTt9Cgkuc3Qze2ZpbGw6IzQ2ODg2MDt9Cgkuc3Q0e2ZpbGw6I0UzNTQzMTt9Cjwvc3R5bGU+CjxyZWN0IHg9IjI2LjciIHk9IjcwIiBjbGFzcz0ic3QwIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjIwIi8+CjxyZWN0IHg9IjI2LjciIHk9IjUwIiBjbGFzcz0ic3QxIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjEzLjMiLz4KPHJlY3QgeD0iNDAiIHk9IjUwIiBjbGFzcz0ic3QyIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjIwIi8+CjxyZWN0IHg9IjQwIiB5PSI3Ni43IiBjbGFzcz0ic3QxIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjYuNyIvPgo8cmVjdCB4PSI1My4zIiB5PSI1MCIgY2xhc3M9InN0MCIgd2lkdGg9IjYuNyIgaGVpZ2h0PSI2LjciLz4KPHJlY3QgeD0iNTMuMyIgeT0iNjMuMyIgY2xhc3M9InN0MSIgd2lkdGg9IjYuNyIgaGVpZ2h0PSIxMy4zIi8+CjxyZWN0IHg9IjUzLjMiIHk9IjgzLjMiIGNsYXNzPSJzdDIiIHdpZHRoPSI2LjciIGhlaWdodD0iNi43Ii8+CjxyZWN0IHg9IjY2LjciIHk9IjUwIiBjbGFzcz0ic3QzIiB3aWR0aD0iNi43IiBoZWlnaHQ9IjEzLjMiLz4KPHJlY3QgeD0iNjYuNyIgeT0iNzAiIGNsYXNzPSJzdDEiIHdpZHRoPSI2LjciIGhlaWdodD0iMjAiLz4KPHBhdGggY2xhc3M9InN0NCIgZD0iTTUzLjMsMjAuM1YxMGgtNi43djEwLjNjLTExLjMsMS42LTIwLDExLjMtMjAsMjMuMWg0Ni43QzczLjMsMzEuNiw2NC42LDIxLjksNTMuMywyMC4zeiIvPgo8L3N2Zz4K"
+ }
+ },
+ "type": "value"
+ }
+ ]
+ },
+ {
+ "id": "custom.align",
+ "value": "center"
+ },
+ {
+ "id": "custom.width",
+ "value": 55
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Last State Time"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "dateTimeAsUS"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Monthly KWH (Average)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "kwatth"
+ },
+ {
+ "id": "decimals",
+ "value": 2
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Watts (Current)"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "watt"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Last State"
+ },
+ "properties": [
+ {
+ "id": "custom.displayMode",
+ "value": "color-background"
+ },
+ {
+ "id": "mappings",
+ "value": [
+ {
+ "options": {
+ "Idle": {
+ "color": "light-blue",
+ "index": 2
+ },
+ "Off": {
+ "color": "dark-blue",
+ "index": 1
+ },
+ "On": {
+ "color": "dark-red",
+ "index": 0
+ }
+ },
+ "type": "value"
+ },
+ {
+ "options": {
+ "match": "null+nan",
+ "result": {
+ "color": "transparent",
+ "index": 3
+ }
+ },
+ "type": "special"
+ }
+ ]
+ },
+ {
+ "id": "custom.align",
+ "value": "center"
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 12,
+ "w": 7,
+ "x": 17,
+ "y": 16
+ },
+ "hideTimeOverride": true,
+ "id": 14,
+ "maxDataPoints": 1,
+ "options": {
+ "showHeader": true,
+ "sortBy": [
+ {
+ "desc": true,
+ "displayName": "Cost"
+ }
+ ]
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "query": "SELECT last(*) FROM \"sense_devices\" WHERE (\"name\" =~ /^$devices$/) and $timeFilter GROUP BY time(1d), \"name\" fill(null)",
+ "rawQuery": true,
+ "refId": "A",
+ "resultFormat": "table"
+ }
+ ],
+ "timeFrom": "1m",
+ "title": "Device Details - Yearly",
+ "transformations": [
+ {
+ "id": "organize",
+ "options": {
+ "excludeByName": {
+ "Time": true,
+ "last_ao_w": true,
+ "last_avg_duration": true,
+ "last_avg_monthly_KWH": true,
+ "last_avg_monthly_cost": true,
+ "last_avg_monthly_pct": true,
+ "last_avg_monthly_runs": true,
+ "last_avg_watts": true,
+ "last_current_ao_wattage": true,
+ "last_current_month_KWH": true,
+ "last_current_month_cost": true,
+ "last_current_month_runs": true,
+ "last_current_watts": true,
+ "last_e": true,
+ "last_i": true,
+ "last_icon": false,
+ "last_last_state": true,
+ "last_last_state_time_epoch": true,
+ "last_v": true,
+ "last_watts": false,
+ "last_yearly_KWH": false,
+ "last_yearly_cost": false,
+ "last_yearly_text": false
+ },
+ "indexByName": {
+ "Time": 0,
+ "last_avg_duration": 4,
+ "last_avg_monthly_KWH": 5,
+ "last_avg_monthly_cost": 6,
+ "last_avg_monthly_pct": 7,
+ "last_avg_monthly_runs": 8,
+ "last_avg_watts": 9,
+ "last_current_ao_wattage": 10,
+ "last_current_month_KWH": 11,
+ "last_current_month_cost": 12,
+ "last_current_month_runs": 13,
+ "last_current_watts": 3,
+ "last_icon": 2,
+ "last_last_state": 14,
+ "last_last_state_time_epoch": 15,
+ "last_yearly_KWH": 16,
+ "last_yearly_cost": 17,
+ "last_yearly_text": 18,
+ "name": 1
+ },
+ "renameByName": {
+ "last_ao_w": "Watts (AO)",
+ "last_avg_duration": "Duration (Average)",
+ "last_avg_monthly_KWH": "Monthly KWH (Average)",
+ "last_avg_monthly_cost": "Monthly Cost (Average)",
+ "last_avg_monthly_pct": "Monthly Percent (Average)",
+ "last_avg_monthly_runs": "Monthly Runs (Average)",
+ "last_avg_watts": "Watts (Average)",
+ "last_current_ao_wattage": "Always On Wattage (Average)",
+ "last_current_month_KWH": "KWH (Current Month)",
+ "last_current_month_cost": "Cost (Current Month)",
+ "last_current_month_runs": "Runs (Current Month)",
+ "last_current_watts": "Watts (Current)",
+ "last_e": "Who Knows",
+ "last_i": "Amps",
+ "last_icon": "Icon",
+ "last_last_state": "Last State",
+ "last_last_state_time_epoch": "Last State Time",
+ "last_v": "Volts",
+ "last_watts": "Watts",
+ "last_yearly_KWH": "kWh",
+ "last_yearly_cost": "Cost",
+ "last_yearly_text": "Text",
+ "name": "Name"
+ }
+ }
+ }
+ ],
+ "type": "table"
+ },
+ {
+ "collapsed": true,
+ "datasource": null,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 28
+ },
+ "id": 17,
+ "panels": [
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 2,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "links": [],
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "volt"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 15,
+ "w": 8,
+ "x": 0,
+ "y": 29
+ },
+ "id": 15,
+ "interval": "$interval",
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "pluginVersion": "8.0.4",
+ "repeat": null,
+ "targets": [
+ {
+ "alias": "$tag_name",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "name"
+ ],
+ "type": "tag"
+ }
+ ],
+ "measurement": "sense_devices",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT mean(\"value\") FROM \"devices\" WHERE (\"name\" =~ /^$Device$/) AND $timeFilter GROUP BY time(3s), \"name\" fill(previous)",
+ "rawQuery": false,
+ "refId": "B",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "v"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "name",
+ "operator": "=~",
+ "value": "/^$plugs$/"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Volts By Plugs",
+ "type": "timeseries"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 2,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "links": [],
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "amp"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 15,
+ "w": 8,
+ "x": 8,
+ "y": 29
+ },
+ "id": 18,
+ "interval": "$interval",
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "pluginVersion": "8.0.4",
+ "targets": [
+ {
+ "alias": "$tag_name",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "name"
+ ],
+ "type": "tag"
+ }
+ ],
+ "measurement": "sense_devices",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT mean(\"value\") FROM \"devices\" WHERE (\"name\" =~ /^$Device$/) AND $timeFilter GROUP BY time(3s), \"name\" fill(previous)",
+ "rawQuery": false,
+ "refId": "B",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "i"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "name",
+ "operator": "=~",
+ "value": "/^$plugs$/"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Amps By Plugs",
+ "type": "timeseries"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 2,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "links": [],
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 15,
+ "w": 8,
+ "x": 16,
+ "y": 29
+ },
+ "id": 19,
+ "interval": "$interval",
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "pluginVersion": "8.0.4",
+ "targets": [
+ {
+ "alias": "$tag_name",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "name"
+ ],
+ "type": "tag"
+ }
+ ],
+ "measurement": "sense_devices",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT mean(\"value\") FROM \"devices\" WHERE (\"name\" =~ /^$Device$/) AND $timeFilter GROUP BY time(3s), \"name\" fill(previous)",
+ "rawQuery": false,
+ "refId": "B",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "e"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "name",
+ "operator": "=~",
+ "value": "/^$plugs$/"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Who Knows By Plugs",
+ "type": "timeseries"
+ }
+ ],
+ "repeat": null,
+ "title": "Plug Details",
+ "type": "row"
+ },
+ {
+ "collapsed": true,
+ "datasource": null,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 29
+ },
+ "id": 23,
+ "panels": [
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "watt"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 10,
+ "w": 12,
+ "x": 0,
+ "y": 30
+ },
+ "id": 21,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "targets": [
+ {
+ "alias": "$tag_name",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "name"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "null"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "sense_devices",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "ao_w"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "always_on",
+ "operator": "=",
+ "value": "true"
+ }
+ ]
+ }
+ ],
+ "title": "Always On Watts (Stacked)",
+ "type": "timeseries"
+ }
+ ],
+ "title": "Always On Devices",
+ "type": "row"
+ }
+ ],
+ "refresh": "1m",
+ "schemaVersion": 30,
+ "style": "dark",
+ "tags": [
+ "influxdb",
+ "sense-collector"
+ ],
+ "templating": {
+ "list": [
+ {
+ "allValue": null,
+ "current": {},
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "definition": "SHOW TAG VALUES FROM \"sense_devices\" WITH KEY = \"name\"",
+ "description": null,
+ "error": null,
+ "hide": 0,
+ "includeAll": true,
+ "label": "Device",
+ "multi": true,
+ "name": "devices",
+ "options": [],
+ "query": "SHOW TAG VALUES FROM \"sense_devices\" WITH KEY = \"name\"",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 5,
+ "tagValuesQuery": "",
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ },
+ {
+ "allValue": null,
+ "current": {},
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "definition": "SHOW TAG VALUES FROM \"sense_devices\" WITH KEY = \"name\" WHERE \"is_plug\" = 'true'",
+ "description": null,
+ "error": null,
+ "hide": 0,
+ "includeAll": true,
+ "label": "Plugs",
+ "multi": true,
+ "name": "plugs",
+ "options": [],
+ "query": "SHOW TAG VALUES FROM \"sense_devices\" WITH KEY = \"name\" WHERE \"is_plug\" = 'true'",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 5,
+ "tagValuesQuery": "",
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ },
+ {
+ "auto": true,
+ "auto_count": 4,
+ "auto_min": "5s",
+ "current": {
+ "selected": false,
+ "text": "1m",
+ "value": "1m"
+ },
+ "description": null,
+ "error": null,
+ "hide": 0,
+ "label": "Interval",
+ "name": "interval",
+ "options": [
+ {
+ "selected": false,
+ "text": "auto",
+ "value": "$__auto_interval_interval"
+ },
+ {
+ "selected": false,
+ "text": "1s",
+ "value": "1s"
+ },
+ {
+ "selected": false,
+ "text": "5s",
+ "value": "5s"
+ },
+ {
+ "selected": false,
+ "text": "10s",
+ "value": "10s"
+ },
+ {
+ "selected": false,
+ "text": "15s",
+ "value": "15s"
+ },
+ {
+ "selected": false,
+ "text": "30s",
+ "value": "30s"
+ },
+ {
+ "selected": true,
+ "text": "1m",
+ "value": "1m"
+ },
+ {
+ "selected": false,
+ "text": "5m",
+ "value": "5m"
+ },
+ {
+ "selected": false,
+ "text": "10m",
+ "value": "10m"
+ },
+ {
+ "selected": false,
+ "text": "15m",
+ "value": "15m"
+ },
+ {
+ "selected": false,
+ "text": "1h",
+ "value": "1h"
+ },
+ {
+ "selected": false,
+ "text": "3h",
+ "value": "3h"
+ }
+ ],
+ "query": "1s,5s,10s,15s,30s,1m,5m,10m,15m,1h,3h",
+ "queryValue": "",
+ "refresh": 2,
+ "skipUrlSync": false,
+ "type": "interval"
+ }
+ ]
+ },
+ "time": {
+ "from": "now/d",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": [
+ "5s",
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ]
+ },
+ "timezone": "",
+ "title": "Sense Collector - Device Overview",
+ "uid": "lux4rd0labs_sense_02",
+ "version": 1,
+ "description": "Sense Collector provides a way of collecting real-time data from the Sense Energy Monitor. These Grafana dashboards offer visualizations for detected devices and smart plugs and their wattage, voltage, and amp utilization."
+}
\ No newline at end of file
diff --git a/grafana/shared/sense_collector-mains_overview.json b/grafana/shared/sense_collector-mains_overview.json
new file mode 100644
index 0000000..9967bf5
--- /dev/null
+++ b/grafana/shared/sense_collector-mains_overview.json
@@ -0,0 +1,807 @@
+{
+ "__inputs": [
+ {
+ "name": "DS_INFLUXDB_- SENSE",
+ "label": "InfluxDB - sense",
+ "description": "",
+ "type": "datasource",
+ "pluginId": "influxdb",
+ "pluginName": "InfluxDB"
+ }
+ ],
+ "__requires": [
+ {
+ "type": "grafana",
+ "id": "grafana",
+ "name": "Grafana",
+ "version": "8.0.6"
+ },
+ {
+ "type": "datasource",
+ "id": "influxdb",
+ "name": "InfluxDB",
+ "version": "1.0.0"
+ },
+ {
+ "type": "panel",
+ "id": "timeseries",
+ "name": "Time series",
+ "version": ""
+ }
+ ],
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "target": {
+ "limit": 100,
+ "matchAny": false,
+ "tags": [],
+ "type": "dashboard"
+ },
+ "type": "dashboard"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "enable": false,
+ "iconColor": "#e35431",
+ "name": "Device Status - On",
+ "query": "SELECT body,device_state FROM \"sense_event\" WHERE (\"event_type\" = 'new_timeline') and $timeFilter and device_state='DeviceOn'",
+ "tagsColumn": "device_state",
+ "textColumn": "body"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "enable": false,
+ "iconColor": "#b7b7b7",
+ "name": "Device Status - Off",
+ "query": "SELECT body,device_state FROM \"sense_event\" WHERE (\"event_type\" = 'new_timeline') and $timeFilter and device_state='DeviceOff'",
+ "tagsColumn": "device_state",
+ "textColumn": "body"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "enable": false,
+ "iconColor": "#77d38e",
+ "name": "Device Status - Idle",
+ "query": "SELECT body,device_state FROM \"sense_event\" WHERE (\"event_type\" = 'new_timeline') and $timeFilter and device_state='DeviceIdle'",
+ "tagsColumn": "device_state",
+ "textColumn": "body"
+ }
+ ]
+ },
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "id": null,
+ "iteration": 1626665955972,
+ "links": [
+ {
+ "asDropdown": true,
+ "icon": "external link",
+ "includeVars": true,
+ "keepTime": true,
+ "tags": [
+ "sense-collector"
+ ],
+ "targetBlank": false,
+ "title": "Sense Collector - Dashboards",
+ "tooltip": "",
+ "type": "dashboards",
+ "url": ""
+ }
+ ],
+ "panels": [
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "links": [],
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "watt"
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "L1"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "blue",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "L2"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "red",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Total"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "rgba(145, 145, 145, 0.81)",
+ "mode": "fixed"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 10,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 4,
+ "interval": "$interval",
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "lastNotNull",
+ "max"
+ ],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "pluginVersion": "8.0.4",
+ "targets": [
+ {
+ "alias": "$tag_leg",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "leg"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "previous"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "sense_mains",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT mean(\"value\") FROM \"mains\" WHERE (\"type\" = 'watts' AND \"name\" =~ /^$Mains$/) AND $timeFilter GROUP BY time(3s), \"name\" fill(previous)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "watts"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "leg",
+ "operator": "=~",
+ "value": "/^$mains$/"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Wattage (Stacked)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 30,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "decimals": 2,
+ "links": [],
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "volt"
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "L1"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "dark-blue",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "L2"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "red",
+ "mode": "fixed"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 10,
+ "w": 24,
+ "x": 0,
+ "y": 10
+ },
+ "id": 9,
+ "interval": "$interval",
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull",
+ "max",
+ "min"
+ ],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "single"
+ }
+ },
+ "pluginVersion": "8.0.4",
+ "targets": [
+ {
+ "alias": "$tag_leg",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "leg"
+ ],
+ "type": "tag"
+ },
+ {
+ "params": [
+ "previous"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "sense_mains",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT mean(\"value\") FROM \"mains\" WHERE (\"type\" = 'volts' AND \"name\" =~ /^$Mains$/) AND $timeFilter GROUP BY time(3s), \"name\" fill(previous)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "voltage"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "leg",
+ "operator": "=~",
+ "value": "/^$mains$/"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Voltages",
+ "type": "timeseries"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 50,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 0,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "decimals": 3,
+ "links": [],
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "hertz"
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Hertz"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "dark-red",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Hertz2"
+ },
+ "properties": [
+ {
+ "id": "color",
+ "value": {
+ "fixedColor": "dark-blue",
+ "mode": "fixed"
+ }
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Hertz2"
+ },
+ "properties": [
+ {
+ "id": "custom.fillOpacity",
+ "value": 0
+ },
+ {
+ "id": "custom.lineWidth",
+ "value": 1
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 10,
+ "w": 24,
+ "x": 0,
+ "y": 20
+ },
+ "id": 2,
+ "interval": "$interval",
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "lastNotNull",
+ "max",
+ "min"
+ ],
+ "displayMode": "hidden",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "single"
+ }
+ },
+ "pluginVersion": "8.0.4",
+ "targets": [
+ {
+ "alias": "Hertz",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "previous"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "sense_mains",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT mean(\"value\") FROM \"mains\" WHERE (\"name\" = 'Hz') AND $timeFilter GROUP BY time(3s) fill(previous)",
+ "rawQuery": false,
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "hz"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": []
+ },
+ {
+ "alias": "Hertz2",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "previous"
+ ],
+ "type": "fill"
+ }
+ ],
+ "measurement": "sense_mains",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "query": "SELECT mean(\"value\") FROM \"mains\" WHERE (\"name\" = 'Hz') AND $timeFilter GROUP BY time(3s) fill(previous)",
+ "rawQuery": false,
+ "refId": "B",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "hz"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Frequency",
+ "type": "timeseries"
+ }
+ ],
+ "refresh": "1m",
+ "schemaVersion": 30,
+ "style": "dark",
+ "tags": [
+ "influxdb",
+ "sense-collector"
+ ],
+ "templating": {
+ "list": [
+ {
+ "allValue": null,
+ "current": {},
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "definition": "SHOW TAG VALUES FROM \"sense_devices\" WITH KEY = \"name\"",
+ "description": null,
+ "error": null,
+ "hide": 2,
+ "includeAll": true,
+ "label": "Device",
+ "multi": true,
+ "name": "devices",
+ "options": [],
+ "query": "SHOW TAG VALUES FROM \"sense_devices\" WITH KEY = \"name\"",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 1,
+ "tagValuesQuery": "",
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ },
+ {
+ "allValue": "",
+ "current": {},
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "definition": "SHOW TAG VALUES FROM \"sense_mains\" WITH KEY = \"leg\"",
+ "description": null,
+ "error": null,
+ "hide": 0,
+ "includeAll": true,
+ "label": "Legs",
+ "multi": false,
+ "name": "mains",
+ "options": [],
+ "query": "SHOW TAG VALUES FROM \"sense_mains\" WITH KEY = \"leg\"",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 1,
+ "type": "query"
+ },
+ {
+ "auto": true,
+ "auto_count": 4,
+ "auto_min": "5s",
+ "current": {
+ "selected": true,
+ "text": "1m",
+ "value": "1m"
+ },
+ "description": null,
+ "error": null,
+ "hide": 0,
+ "label": "Interval",
+ "name": "interval",
+ "options": [
+ {
+ "selected": false,
+ "text": "auto",
+ "value": "$__auto_interval_interval"
+ },
+ {
+ "selected": false,
+ "text": "1s",
+ "value": "1s"
+ },
+ {
+ "selected": false,
+ "text": "5s",
+ "value": "5s"
+ },
+ {
+ "selected": false,
+ "text": "10s",
+ "value": "10s"
+ },
+ {
+ "selected": false,
+ "text": "15s",
+ "value": "15s"
+ },
+ {
+ "selected": false,
+ "text": "30s",
+ "value": "30s"
+ },
+ {
+ "selected": true,
+ "text": "1m",
+ "value": "1m"
+ },
+ {
+ "selected": false,
+ "text": "5m",
+ "value": "5m"
+ },
+ {
+ "selected": false,
+ "text": "10m",
+ "value": "10m"
+ },
+ {
+ "selected": false,
+ "text": "15m",
+ "value": "15m"
+ },
+ {
+ "selected": false,
+ "text": "1h",
+ "value": "1h"
+ },
+ {
+ "selected": false,
+ "text": "3h",
+ "value": "3h"
+ }
+ ],
+ "query": "1s,5s,10s,15s,30s,1m,5m,10m,15m,1h,3h",
+ "queryValue": "",
+ "refresh": 2,
+ "skipUrlSync": false,
+ "type": "interval"
+ }
+ ]
+ },
+ "time": {
+ "from": "now/d",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": [
+ "5s",
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ]
+ },
+ "timezone": "",
+ "title": "Sense Collector - Mains Overview",
+ "uid": "lux4rd0labs_sense_03",
+ "version": 1,
+ "description": "Sense Collector provides a way of collecting real-time data from the Sense Energy Monitor. These Grafana dashboards offer visualizations for detected devices and smart plugs and their wattage, voltage, and amp utilization."
+}
\ No newline at end of file
diff --git a/grafana/shared/sense_collector-monitor_and_detection.json b/grafana/shared/sense_collector-monitor_and_detection.json
new file mode 100644
index 0000000..0deda35
--- /dev/null
+++ b/grafana/shared/sense_collector-monitor_and_detection.json
@@ -0,0 +1,975 @@
+{
+ "__inputs": [
+ {
+ "name": "DS_INFLUXDB_- SENSE",
+ "label": "InfluxDB - sense",
+ "description": "",
+ "type": "datasource",
+ "pluginId": "influxdb",
+ "pluginName": "InfluxDB"
+ }
+ ],
+ "__requires": [
+ {
+ "type": "grafana",
+ "id": "grafana",
+ "name": "Grafana",
+ "version": "8.0.6"
+ },
+ {
+ "type": "datasource",
+ "id": "influxdb",
+ "name": "InfluxDB",
+ "version": "1.0.0"
+ },
+ {
+ "type": "panel",
+ "id": "table",
+ "name": "Table",
+ "version": ""
+ },
+ {
+ "type": "panel",
+ "id": "timeseries",
+ "name": "Time series",
+ "version": ""
+ }
+ ],
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "id": null,
+ "iteration": 1626665965748,
+ "links": [
+ {
+ "asDropdown": true,
+ "icon": "external link",
+ "includeVars": true,
+ "keepTime": true,
+ "tags": [
+ "sense-collector"
+ ],
+ "targetBlank": false,
+ "title": "Sense Collector - Dashboards",
+ "tooltip": "",
+ "type": "dashboards",
+ "url": ""
+ }
+ ],
+ "panels": [
+ {
+ "datasource": null,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 11,
+ "title": "Device Detection Status",
+ "type": "row"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineStyle": {
+ "fill": "solid"
+ },
+ "lineWidth": 1,
+ "pointSize": 20,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "max": 100,
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "percent"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 11,
+ "w": 12,
+ "x": 0,
+ "y": 1
+ },
+ "id": 6,
+ "interval": "$interval",
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "targets": [
+ {
+ "alias": "$tag_name",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "name"
+ ],
+ "type": "tag"
+ }
+ ],
+ "measurement": "sense_monitor_device_detection",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "progress"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "detection_type",
+ "operator": "=",
+ "value": "found"
+ }
+ ]
+ }
+ ],
+ "timeFrom": null,
+ "title": "Found Devices",
+ "type": "timeseries"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 20,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "max": 100,
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "percent"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 11,
+ "w": 12,
+ "x": 12,
+ "y": 1
+ },
+ "id": 7,
+ "interval": "$interval",
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "targets": [
+ {
+ "alias": "$tag_name",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "name"
+ ],
+ "type": "tag"
+ }
+ ],
+ "measurement": "sense_monitor_device_detection",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "progress"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "detection_type",
+ "operator": "=",
+ "value": "in_progress"
+ }
+ ]
+ }
+ ],
+ "title": "In Progress Devices",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "datasource": null,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 12
+ },
+ "id": 9,
+ "panels": [],
+ "title": "Monitor Details",
+ "type": "row"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "fixedColor": "dark-orange",
+ "mode": "fixed"
+ },
+ "custom": {
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "hue",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 20,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "max": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "dBm"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 10,
+ "w": 24,
+ "x": 0,
+ "y": 13
+ },
+ "id": 4,
+ "interval": "$interval",
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom"
+ },
+ "tooltip": {
+ "mode": "multi"
+ }
+ },
+ "targets": [
+ {
+ "alias": "$tag_serial",
+ "groupBy": [
+ {
+ "params": [
+ "$__interval"
+ ],
+ "type": "time"
+ },
+ {
+ "params": [
+ "serial"
+ ],
+ "type": "tag"
+ }
+ ],
+ "measurement": "sense_monitor_status",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "time_series",
+ "select": [
+ [
+ {
+ "params": [
+ "wifi_strength"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "mean"
+ }
+ ]
+ ],
+ "tags": []
+ }
+ ],
+ "timeFrom": null,
+ "title": "Wifi Signal Strength - RSSI",
+ "type": "timeseries"
+ },
+ {
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "align": "center",
+ "displayMode": "auto"
+ },
+ "mappings": [
+ {
+ "options": {
+ "OK": {
+ "color": "dark-green",
+ "index": 2,
+ "text": "OK"
+ },
+ "false": {
+ "color": "dark-red",
+ "index": 1,
+ "text": "False"
+ },
+ "true": {
+ "color": "dark-green",
+ "index": 0,
+ "text": "True"
+ }
+ },
+ "type": "value"
+ }
+ ],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Online"
+ },
+ "properties": [
+ {
+ "id": "custom.displayMode",
+ "value": "color-background"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Ethernet"
+ },
+ "properties": [
+ {
+ "id": "custom.displayMode",
+ "value": "color-background"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Status"
+ },
+ "properties": [
+ {
+ "id": "custom.displayMode",
+ "value": "color-background"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Wifi Strength"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "dBm"
+ },
+ {
+ "id": "thresholds",
+ "value": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "dark-red",
+ "value": null
+ },
+ {
+ "color": "semi-dark-red",
+ "value": -90
+ },
+ {
+ "color": "dark-orange",
+ "value": -80
+ },
+ {
+ "color": "light-orange",
+ "value": -70
+ },
+ {
+ "color": "yellow",
+ "value": -60
+ },
+ {
+ "color": "dark-green",
+ "value": -50
+ }
+ ]
+ }
+ },
+ {
+ "id": "custom.displayMode",
+ "value": "color-background"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "NDT Enabled"
+ },
+ "properties": [
+ {
+ "id": "custom.displayMode",
+ "value": "color-background"
+ }
+ ]
+ },
+ {
+ "matcher": {
+ "id": "byName",
+ "options": "Progress"
+ },
+ "properties": [
+ {
+ "id": "unit",
+ "value": "percent"
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 3,
+ "w": 24,
+ "x": 0,
+ "y": 23
+ },
+ "hideTimeOverride": true,
+ "id": 2,
+ "interval": null,
+ "maxDataPoints": 1,
+ "options": {
+ "showHeader": true
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "groupBy": [
+ {
+ "params": [
+ "1d"
+ ],
+ "type": "time"
+ }
+ ],
+ "measurement": "sense_monitor_status",
+ "orderByTime": "ASC",
+ "policy": "default",
+ "refId": "A",
+ "resultFormat": "table",
+ "select": [
+ [
+ {
+ "params": [
+ "ethernet"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ },
+ {
+ "params": [
+ "ethernet"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "ip_address"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ },
+ {
+ "params": [
+ "ip_address"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "mac"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ },
+ {
+ "params": [
+ "mac"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "ndt_enabled"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ },
+ {
+ "params": [
+ "ndt_enabled"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "online"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ },
+ {
+ "params": [
+ "online"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "progress"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ },
+ {
+ "params": [
+ "progress"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "ssid"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ },
+ {
+ "params": [
+ "ssid"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "status"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ },
+ {
+ "params": [
+ "status"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "version"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ },
+ {
+ "params": [
+ "version"
+ ],
+ "type": "alias"
+ }
+ ],
+ [
+ {
+ "params": [
+ "wifi_strength"
+ ],
+ "type": "field"
+ },
+ {
+ "params": [],
+ "type": "last"
+ },
+ {
+ "params": [
+ "wifi_stength"
+ ],
+ "type": "alias"
+ }
+ ]
+ ],
+ "tags": [
+ {
+ "key": "serial",
+ "operator": "=",
+ "value": "M638000670"
+ }
+ ]
+ }
+ ],
+ "timeFrom": "5m",
+ "title": "Monitor Details",
+ "transformations": [
+ {
+ "id": "organize",
+ "options": {
+ "excludeByName": {
+ "Time": true
+ },
+ "indexByName": {
+ "Time": 0,
+ "ethernet": 8,
+ "ip_address": 4,
+ "mac": 5,
+ "ndt_enabled": 9,
+ "online": 1,
+ "progress": 3,
+ "ssid": 6,
+ "status": 2,
+ "version": 10,
+ "wifi_stength": 7
+ },
+ "renameByName": {
+ "ethernet": "Ethernet",
+ "ip_address": "IP Address",
+ "ip_address 1": "",
+ "mac": "MAC Address",
+ "ndt_enabled": "NDT Enabled",
+ "online": "Online",
+ "progress": "Progress",
+ "ssid": "SSID",
+ "status": "Status",
+ "version": "Version",
+ "wifi_stength": "Wifi Strength"
+ }
+ }
+ }
+ ],
+ "type": "table"
+ }
+ ],
+ "refresh": "1m",
+ "schemaVersion": 30,
+ "style": "dark",
+ "tags": [
+ "sense-collector",
+ "influxdb"
+ ],
+ "templating": {
+ "list": [
+ {
+ "allValue": null,
+ "current": {},
+ "datasource": "${DS_INFLUXDB_- SENSE}",
+ "definition": "SHOW TAG VALUES WITH KEY = \"serial\"",
+ "description": null,
+ "error": null,
+ "hide": 0,
+ "includeAll": false,
+ "label": "Monitor",
+ "multi": false,
+ "name": "serial",
+ "options": [],
+ "query": "SHOW TAG VALUES WITH KEY = \"serial\"",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 5,
+ "type": "query"
+ },
+ {
+ "auto": false,
+ "auto_count": 30,
+ "auto_min": "10s",
+ "current": {
+ "selected": true,
+ "text": "1m",
+ "value": "1m"
+ },
+ "description": null,
+ "error": null,
+ "hide": 0,
+ "label": "Interval",
+ "name": "interval",
+ "options": [
+ {
+ "selected": true,
+ "text": "1m",
+ "value": "1m"
+ },
+ {
+ "selected": false,
+ "text": "5m",
+ "value": "5m"
+ },
+ {
+ "selected": false,
+ "text": "10m",
+ "value": "10m"
+ },
+ {
+ "selected": false,
+ "text": "15m",
+ "value": "15m"
+ },
+ {
+ "selected": false,
+ "text": "1h",
+ "value": "1h"
+ },
+ {
+ "selected": false,
+ "text": "3h",
+ "value": "3h"
+ },
+ {
+ "selected": false,
+ "text": "12h",
+ "value": "12h"
+ },
+ {
+ "selected": false,
+ "text": "1d",
+ "value": "1d"
+ }
+ ],
+ "query": "1m,5m,10m,15m,1h,3h,12h,1d",
+ "queryValue": "",
+ "refresh": 2,
+ "skipUrlSync": false,
+ "type": "interval"
+ }
+ ]
+ },
+ "time": {
+ "from": "now/d",
+ "to": "now"
+ },
+ "timepicker": {},
+ "timezone": "",
+ "title": "Sense Collector - Monitor and Detection",
+ "uid": "lux4rd0labs_sense_03",
+ "version": 1,
+ "description": "Sense Collector provides a way of collecting real-time data from the Sense Energy Monitor. These Grafana dashboards offer visualizations for detected devices and smart plugs and their wattage, voltage, and amp utilization."
+}
\ No newline at end of file
diff --git a/images/readme.md b/images/readme.md
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/images/readme.md
@@ -0,0 +1 @@
+
diff --git a/images/sense_collector-screen_shot-collector_info.jpg b/images/sense_collector-screen_shot-collector_info.jpg
new file mode 100644
index 0000000..fa9fe6a
Binary files /dev/null and b/images/sense_collector-screen_shot-collector_info.jpg differ
diff --git a/images/sense_collector-screen_shot-collector_info.png b/images/sense_collector-screen_shot-collector_info.png
new file mode 100644
index 0000000..21ec9f6
Binary files /dev/null and b/images/sense_collector-screen_shot-collector_info.png differ
diff --git a/images/sense_collector-screen_shot-device_overview.jpg b/images/sense_collector-screen_shot-device_overview.jpg
new file mode 100644
index 0000000..acfd9f7
Binary files /dev/null and b/images/sense_collector-screen_shot-device_overview.jpg differ
diff --git a/images/sense_collector-screen_shot-device_overview.png b/images/sense_collector-screen_shot-device_overview.png
new file mode 100644
index 0000000..bc6adec
Binary files /dev/null and b/images/sense_collector-screen_shot-device_overview.png differ
diff --git a/images/sense_collector-screen_shot-mains_overview.jpg b/images/sense_collector-screen_shot-mains_overview.jpg
new file mode 100644
index 0000000..2cea4e0
Binary files /dev/null and b/images/sense_collector-screen_shot-mains_overview.jpg differ
diff --git a/images/sense_collector-screen_shot-mains_overview.png b/images/sense_collector-screen_shot-mains_overview.png
new file mode 100644
index 0000000..11f263f
Binary files /dev/null and b/images/sense_collector-screen_shot-mains_overview.png differ
diff --git a/images/sense_collector-screen_shot-monitor_and_detection.jpg b/images/sense_collector-screen_shot-monitor_and_detection.jpg
new file mode 100644
index 0000000..e925c75
Binary files /dev/null and b/images/sense_collector-screen_shot-monitor_and_detection.jpg differ
diff --git a/images/sense_collector-screen_shot-monitor_and_detection.png b/images/sense_collector-screen_shot-monitor_and_detection.png
new file mode 100644
index 0000000..4e0fb9b
Binary files /dev/null and b/images/sense_collector-screen_shot-monitor_and_detection.png differ
diff --git a/images/sense_collector_title.png b/images/sense_collector_title.png
new file mode 100644
index 0000000..5c69f86
Binary files /dev/null and b/images/sense_collector_title.png differ
diff --git a/sense-collector/Dockerfile b/sense-collector/Dockerfile
index 9ac547d..ca41e3c 100644
--- a/sense-collector/Dockerfile
+++ b/sense-collector/Dockerfile
@@ -4,8 +4,16 @@ RUN apt-get update && apt-get install -y bc curl dumb-init bash python procps co
RUN mkdir /sense-collector
-COPY exec-sense-collector.sh \
-start.sh \
+COPY exec-device-details.sh \
+exec-host-performance.sh \
+exec-monitor-status.sh \
+exec-sense-collector.sh \
+sense-collector-details.sh \
+start-device-details.sh \
+start-host-performance.sh \
+start-monitor-status.sh \
+start-sense-collector.sh \
+sense-collector-init.sh \
websocat_amd64-linux-static \
/sense-collector/
@@ -21,4 +29,4 @@ RUN chmod +x /usr/bin/jq
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
-CMD ["/sense-collector/start.sh"]
+CMD ["/sense-collector/sense-collector-init.sh"]
diff --git a/sense-collector/build.sh b/sense-collector/build.sh
new file mode 100644
index 0000000..81f87d8
--- /dev/null
+++ b/sense-collector/build.sh
@@ -0,0 +1,5 @@
+docker build . -t lux4rd0/sense-collector:latest -t lux4rd0/sense-collector:$1 -t docker01.tylephony.com:5000/lux4rd0/sense-collector:latest -t docker01.tylephony.com:5000/lux4rd0/sense-collector:$1
+docker push docker01.tylephony.com:5000/lux4rd0/sense-collector:latest
+docker push docker01.tylephony.com:5000/lux4rd0/sense-collector:$1
+docker push lux4rd0/sense-collector:latest
+docker push lux4rd0/sense-collector:$1
diff --git a/sense-collector/exec-device-details.sh b/sense-collector/exec-device-details.sh
new file mode 100644
index 0000000..d32ad32
--- /dev/null
+++ b/sense-collector/exec-device-details.sh
@@ -0,0 +1,261 @@
+#!/bin/bash
+
+##
+## Sense Collector - exec-sense-device-details.sh
+##
+
+##
+## Sense-Collector Details
+##
+
+source sense-collector-details.sh
+
+##
+## Set Specific Variables
+##
+
+collector_type="device-details"
+
+##
+## Start Observations Timer
+##
+
+observations_start=$(date +%s%N)
+
+##
+## Sense-Collector Details
+##
+
+debug=$SENSE_COLLECTOR_DEBUG
+debug_curl=$SENSE_COLLECTOR_DEBUG_CURL
+debug_if=$SENSE_COLLECTOR_DEBUG_IF
+host_hostname=$SENSE_COLLECTOR_HOST_HOSTNAME
+influxdb_password=$SENSE_COLLECTOR_INFLUXDB_PASSWORD
+influxdb_url=$SENSE_COLLECTOR_INFLUXDB_URL
+influxdb_username=$SENSE_COLLECTOR_INFLUXDB_USERNAME
+sense_monitor_id=$SENSE_COLLECTOR_MONITOR_ID
+sense_token=$SENSE_COLLECTOR_TOKEN
+threads=$SENSE_COLLECTOR_THREADS
+
+##
+## Set Threads
+##
+
+if [ -z "${threads}" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_THREADS${echo_normal} environmental variable not set. Defaulting to ${echo_bold}4${echo_normal} threads."; threads="4"; export SENSE_COLLECTOR_THREADS="4"; fi
+
+if [ "$debug" == "true" ]; then
+
+echo "debug=${debug}
+debug_curl=${debug_curl}
+debug_if=${debug_if}
+host_hostname=${host_hostname}
+influxdb_password=${influxdb_password}
+influxdb_url=${influxdb_url}
+influxdb_username=${influxdb_username}
+sense_monitor_id=${sense_monitor_id}
+sense_token=${sense_token}
+threads=${threads}"
+
+fi
+
+##
+## Curl Command
+##
+
+if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --show-error --fail ); fi
+
+url_sense_get_devices="https://api.sense.com/apiservice/api/v1/app/monitors/${sense_monitor_id}/devices"
+
+response_url_sense_get_devices=$(curl "${curl[@]}" -k -H "Sense-Collector-Client-Version: 1.0.0" -H "X-Sense-Protocol: 3" -H "User-Agent: okhttp/3.8.0" -H "Authorization: bearer ${sense_token}" "${url_sense_get_devices}")
+
+#echo "${response_url_sense_get_devices}"
+
+devices=($(echo "${response_url_sense_get_devices}" | jq -r '.[].id | @sh' | tr -d \'))
+
+number_of_devices=$(echo "${response_url_sense_get_devices}" | jq '. | length')
+
+number_of_devices_minus_one=$((number_of_devices-1))
+
+#echo "number_of_devices=${number_of_devices}"
+#echo "devices=${devices[@]}"
+
+##
+## Start "threading"
+##
+
+for device_number in $(seq 0 $number_of_devices_minus_one) ; do
+
+#(
+
+url_sense_device="https://api.sense.com/apiservice/api/v1/app/monitors/${sense_monitor_id}/devices/${devices[device_number]}"
+
+#echo "url_sense_device=${url_sense_device}"
+#echo "device_id=${devices[device_number]}"
+
+if [ "${devices[device_number]}" == "unknown" ] || [ "${devices[device_number]}" == "always_on" ] || [ "${devices[device_number]}" == "Other" ]; then continue; fi
+
+if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --show-error --fail ); fi
+
+response_url_sense_device=$(curl "${curl[@]}" -k -H "Sense-Collector-Client-Version: 1.0.0" -H "X-Sense-Protocol: 3" -H "User-Agent: okhttp/3.8.0" -H "Authorization: bearer ${sense_token}" "${url_sense_device}")
+
+#name=$(echo "${response_url_sense_device}" | jq -r .device.name)
+
+eval "$(echo "${response_url_sense_device}" | jq -r '.usage | {"current_month_runs", "current_month_KWH", "avg_monthly_runs", "avg_monthly_KWH", "avg_monthly_pct", "avg_watts", "avg_duration", "yearly_KWH", "yearly_text", "yearly_cost", "current_month_cost", "avg_monthly_cost"} | to_entries | .[] | .key + "=" + "\"" + ( .value|tostring ) + "\""')"
+
+if [ "$debug" == "true" ]; then
+
+echo "
+current_month_runs=${current_month_runs}
+current_month_KWH=${current_month_KWH}
+avg_monthly_runs=${avg_monthly_runs}
+avg_monthly_KWH=${avg_monthly_KWH}
+avg_monthly_pct=${avg_monthly_pct}
+avg_watts=${avg_watts}
+avg_duration=${avg_duration}
+yearly_KWH=${yearly_KWH}
+yearly_text=${yearly_text}
+yearly_cost=${yearly_cost}
+current_month_cost=${current_month_cost}
+avg_monthly_cost=${avg_monthly_cost}"
+
+fi
+
+eval "$(echo "${response_url_sense_device}" | jq -r '.device | {"name", "icon", "last_state", "last_state_time"}' | jq -r '. | to_entries | .[] | .key + "=" + "\"" + ( .value|tostring ) + "\""')"
+
+if [ "${last_state_time}" != "null" ]; then last_state_time_epoch=$(date -d"${last_state_time}" +%s%3N ); else if [ "$debug_if" == "true" ]; then echo "last_state_time is null"; fi; fi
+if [ "${last_state_time}" != "null" ]; then last_state_time_ns=$(date -d"${last_state_time}" +%s%N ); else if [ "$debug_if" == "true" ]; then echo "last_state_time is null"; fi; fi
+
+if [ "$debug" == "true" ]; then
+
+echo "
+name=${name}
+icon=${icon}
+last_state=${last_state}
+last_state_time=${last_state_time}
+last_state_time_epoch=${last_state_time_epoch}
+last_state_time_ns=${last_state_time_ns}
+"
+
+fi
+
+if [ "$debug" == "true" ]; then echo "loop=${device_number} - name=${name} - device_id=${devices[$device_number]}"; fi
+
+##
+## Escape Names (Function)
+##
+
+escape_names
+
+curl_message="sense_devices,device_id=${devices[device_number]},name=${name_escaped} "
+
+if [ "${current_month_runs}" != "null" ]; then curl_message="${curl_message}current_month_runs=${current_month_runs},"; else if [ "$debug_if" == "true" ]; then echo "name=${name} current_month_runs is null"; fi; fi
+if [ "${current_month_KWH}" != "null" ]; then curl_message="${curl_message}current_month_KWH=${current_month_KWH},"; else if [ "$debug_if" == "true" ]; then echo "name=${name} current_month_KWH is null"; fi; fi
+if [ "${avg_monthly_runs}" != "null" ]; then curl_message="${curl_message}avg_monthly_runs=${avg_monthly_runs},"; else if [ "$debug_if" == "true" ]; then echo "name=${name} avg_monthly_runs is null"; fi; fi
+if [ "${avg_monthly_KWH}" != "null" ]; then curl_message="${curl_message}avg_monthly_KWH=${avg_monthly_KWH},"; else if [ "$debug_if" == "true" ]; then echo "name=${name} avg_monthly_KWH is null"; fi; fi
+if [ "${avg_monthly_pct}" != "null" ]; then curl_message="${curl_message}avg_monthly_pct=${avg_monthly_pct},"; else if [ "$debug_if" == "true" ]; then echo "name=${name} avg_monthly_pct is null"; fi; fi
+if [ "${avg_watts}" != "null" ]; then curl_message="${curl_message}avg_watts=${avg_watts},"; else if [ "$debug_if" == "true" ]; then echo "name=${name} avg_watts is null"; fi; fi
+if [ "${avg_duration}" != "null" ]; then curl_message="${curl_message}avg_duration=${avg_duration},"; else if [ "$debug_if" == "true" ]; then echo "name=${name} avg_duration is null"; fi; fi
+if [ "${yearly_KWH}" != "null" ]; then curl_message="${curl_message}yearly_KWH=${yearly_KWH},"; else if [ "$debug_if" == "true" ]; then echo "name=${name} yearly_KWH is null"; fi; fi
+if [ "${yearly_text}" != "null" ]; then curl_message="${curl_message}yearly_text=\"${yearly_text}\","; else if [ "$debug_if" == "true" ]; then echo "name=${name} yearly_text is null"; fi; fi
+if [ "${yearly_cost}" != "null" ]; then yearly_cost_dollars=$(echo "scale=2; ${yearly_cost}/100" | bc); curl_message="${curl_message}yearly_cost=${yearly_cost_dollars},"; else if [ "$debug_if" == "true" ]; then echo "name=${name} yearly_cost is null"; fi; fi
+if [ "${current_month_cost}" != "null" ]; then current_month_cost_dollars=$(echo "scale=2; ${current_month_cost}/100" | bc); curl_message="${curl_message}current_month_cost=${current_month_cost_dollars},"; else if [ "$debug_if" == "true" ]; then echo "name=${name} current_month_cost is null"; fi; fi
+if [ "${icon}" != "null" ]; then curl_message="${curl_message}icon=\"${icon}\","; else if [ "$debug_if" == "true" ]; then echo "name=${name} icon is null"; fi; fi
+if [ "${last_state}" != "null" ]; then curl_message="${curl_message}last_state=\"${last_state}\","; else if [ "$debug_if" == "true" ]; then echo "name=${name} last_state is ${last_state} - null"; fi; fi
+if [ -n "${last_state_time_epoch}" ]; then curl_message="${curl_message}last_state_time_epoch=${last_state_time_epoch},"; else if [ "$debug_if" == "true" ]; then echo "name=${name} last_state_time_epoch is empty"; fi; fi
+
+##
+## Remove a trailing comma in curl_message if the last element happens to be null (so that there's still a properly formatted InfluxDB mmessage)
+##
+
+curl_message="$(echo "${curl_message}" | sed 's/,$//')"
+
+if [ "$debug" == "true" ]; then echo "${curl_message}"; fi
+
+if [ "$curl_debug" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
+
+/usr/bin/timeout -k 1 10s curl "${curl[@]}" --connect-timeout 2 --max-time 2 --retry 5 --retry-delay 0 --retry-max-time 30 -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "${curl_message}" &
+
+#) &
+
+#if [[ $(jobs -r -p | wc -l) -ge $threads ]]; then wait -n; fi
+
+done
+
+#wait
+
+if [ "$debug" == "true" ]; then echo "Device Loop Finished"; fi
+
+##
+## End "threading"
+##
+
+##
+## Always On
+##
+
+url_sense_device="https://api.sense.com/apiservice/api/v1/app/monitors/${sense_monitor_id}/devices/always_on"
+
+if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --show-error --fail ); fi
+
+response_url_sense_device=$(curl "${curl[@]}" -k -H "Sense-Collector-Client-Version: 1.0.0" -H "X-Sense-Protocol: 3" -H "User-Agent: okhttp/3.8.0" -H "Authorization: bearer ${sense_token}" "${url_sense_device}")
+
+eval "$(echo "${response_url_sense_device}" | jq -r '.usage | {"avg_monthly_KWH", "avg_monthly_pct", "avg_watts", "yearly_KWH", "yearly_text", "yearly_cost", "avg_monthly_cost", "current_ao_wattage"} | to_entries | .[] | .key + "=" + "\"" + ( .value|tostring ) + "\""')"
+
+if [ "$debug" == "true" ]; then
+
+echo "
+avg_monthly_KWH=${avg_monthly_KWH}
+avg_monthly_pct=${avg_monthly_pct}
+avg_watts=${avg_watts}
+yearly_KWH=${yearly_KWH}
+yearly_text=${yearly_text}
+yearly_cost=${yearly_cost}
+avg_monthly_cost=${avg_monthly_cost}
+current_ao_wattage=${current_ao_wattage}
+"
+fi
+
+curl_message="sense_devices,device_id=${devices[device_number]},name=Always\ On "
+
+if [ "${avg_monthly_KWH}" != "null" ]; then curl_message="${curl_message}avg_monthly_KWH=${avg_monthly_KWH},"; else if [ "$debug_if" == "true" ]; then echo "avg_monthly_KWH is null"; fi; fi
+if [ "${avg_monthly_pct}" != "null" ]; then curl_message="${curl_message}avg_monthly_pct=${avg_monthly_pct},"; else if [ "$debug_if" == "true" ]; then echo "avg_monthly_pct is null"; fi; fi
+if [ "${avg_watts}" != "null" ]; then curl_message="${curl_message}avg_watts=${avg_watts},"; else if [ "$debug_if" == "true" ]; then echo "avg_watts is null"; fi; fi
+if [ "${yearly_KWH}" != "null" ]; then curl_message="${curl_message}yearly_KWH=${yearly_KWH},"; else if [ "$debug_if" == "true" ]; then echo "yearly_KWH is null"; fi; fi
+if [ "${yearly_text}" != "null" ]; then curl_message="${curl_message}yearly_text=\"${yearly_text}\","; else if [ "$debug_if" == "true" ]; then echo "yearly_text is null"; fi; fi
+if [ "${yearly_cost}" != "null" ]; yearly_cost_dollars=$(echo "scale=2; ${yearly_cost}/100" | bc); then curl_message="${curl_message}yearly_cost=${yearly_cost_dollars},"; else if [ "$debug_if" == "true" ]; then echo "yearly_cost is null"; fi; fi
+if [ "${avg_monthly_cost}" != "null" ]; then curl_message="${curl_message}avg_monthly_cost=${avg_monthly_cost},"; else if [ "$debug_if" == "true" ]; then echo "avg_monthly_cost is null"; fi; fi
+if [ "${current_ao_wattage}" != "null" ]; then curl_message="${curl_message}current_ao_wattage=${current_ao_wattage},"; else if [ "$debug_if" == "true" ]; then echo "current_ao_wattage is null"; fi; fi
+
+##
+## Add our own Icon for Always On
+##
+
+curl_message="${curl_message}icon=\"always_on\""
+
+if [ "$debug" == "true" ]; then echo "${curl_message}"; fi
+
+if [ "$curl_debug" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
+
+/usr/bin/timeout -k 1 10s curl "${curl[@]}" --connect-timeout 2 --max-time 2 --retry 5 --retry-delay 0 --retry-max-time 30 -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "${curl_message}" &
+
+#wait
+
+##
+## End Observations Timer
+##
+
+observations_end=$(date +%s%N)
+observations_duration=$((observations_end-observations_start))
+
+if [ "$debug" == "true" ]; then echo "$(date) - observations_duration:${observations_duration}"; fi
+
+##
+## Send Observations Metrics To InfluxDB
+##
+
+if [ -n "$influxdb_url" ]; then
+
+curl "${curl[@]}" -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "
+sense_o11y,host_hostname=${host_hostname},function="device_details",source=${collector_type} duration=${observations_duration}"
+
+fi
\ No newline at end of file
diff --git a/sense-collector/exec-host-performance.sh b/sense-collector/exec-host-performance.sh
new file mode 100644
index 0000000..1de31dc
--- /dev/null
+++ b/sense-collector/exec-host-performance.sh
@@ -0,0 +1,281 @@
+#!/bin/bash
+
+##
+## Sense Collector - exec-host-performance.sh
+##
+
+##
+## Sense-Collector Details
+##
+
+source sense-collector-details.sh
+
+##
+## Set Specific Variables
+##
+
+collector_type="host-performance"
+
+##
+## Start Observations Timer
+##
+
+observations_start=$(date +%s%N)
+
+debug=$SENSE_COLLECTOR_DEBUG
+debug_curl=$SENSE_COLLECTOR_DEBUG_CURL
+healthcheck=$SENSE_COLLECTOR_HEALTHCHECK
+host_hostname=$SENSE_COLLECTOR_HOST_HOSTNAME
+influxdb_password=$SENSE_COLLECTOR_INFLUXDB_PASSWORD
+influxdb_url=$SENSE_COLLECTOR_INFLUXDB_URL
+influxdb_username=$SENSE_COLLECTOR_INFLUXDB_USERNAME
+
+if [ "$debug" == "true" ]
+
+then
+
+echo "${echo_bold}${echo_color_host_performance}${collector_type}:${echo_normal} $(date) - Starting Sense Collector (exec-host-performance.sh) - https://github.com/lux4rd0/sense-collector
+
+Debug Environmental Variables
+
+collector_type=${collector_type}
+debug=${debug}
+debug_curl=${debug_curl}
+healthcheck=${healthcheck}
+host_hostname=${host_hostname}
+influxdb_password=${influxdb_password}
+influxdb_url=${influxdb_url}
+influxdb_username=${influxdb_username}
+sense_collector_version=${sense_collector_version}"
+
+fi
+
+##
+## Curl Command
+##
+
+if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
+
+##
+## Health Check Function
+##
+
+health_check
+
+##
+## Escape Names (Function)
+##
+
+escape_names
+
+##
+## ╦ ╦┌─┐┌─┐┌┬┐ ╔╦╗┌─┐┌┬┐┬─┐┬┌─┐┌─┐
+## ╠═╣│ │└─┐ │ ║║║├┤ │ ├┬┘││ └─┐
+## ╩ ╩└─┘└─┘ ┴ ╩ ╩└─┘ ┴ ┴└─┴└─┘└─┘
+##
+
+memory=($(free -w))
+
+mem_total=${memory[8]}
+mem_used=${memory[9]}
+mem_free=${memory[10]}
+mem_shared=${memory[11]}
+mem_buffers=${memory[12]}
+mem_cache=${memory[13]}
+mem_available=${memory[14]}
+swap_total=${memory[16]}
+swap_used=${memory[17]}
+swap_free=${memory[18]}
+
+if [ "$debug" == "true" ]
+then
+
+echo "
+mem_total=${mem_total}
+mem_used=${mem_used}
+mem_free=${mem_free}
+mem_shared=${mem_shared}
+mem_buffers=${mem_buffers}
+mem_cache=${mem_cache}
+mem_available=${mem_available}
+swap_total=${swap_total}
+swap_used=${swap_used}
+swap_free=${swap_free}"
+
+fi
+
+cpu=($(mpstat 1 1 | tail -1))
+
+cpu_usr=${cpu[2]}
+cpu_nice=${cpu[3]}
+cpu_sys=${cpu[4]}
+cpu_iowait=${cpu[5]}
+cpu_irq=${cpu[6]}
+cpu_soft=${cpu[7]}
+cpu_steal=${cpu[8]}
+cpu_guest=${cpu[9]}
+cpu_gnice=${cpu[10]}
+cpu_idle=${cpu[11]}
+
+if [ "$debug" == "true" ]
+then
+
+echo "
+cpu_usr=${cpu_usr}
+cpu_nice=${cpu_nice}
+cpu_sys=${cpu_sys}
+cpu_iowait=${cpu_iowait}
+cpu_irq=${cpu_irq}
+cpu_soft=${cpu_soft}
+cpu_steal=${cpu_steal}
+cpu_guest=${cpu_guest}
+cpu_gnice=${cpu_gnice}
+cpu_idle=${cpu_idle}"
+
+fi
+
+loadavg=($(cat /proc/loadavg))
+
+loadavg_one=${loadavg[0]}
+loadavg_five=${loadavg[1]}
+loadavg_fifteen=${loadavg[2]}
+
+if [ "$debug" == "true" ]
+then
+
+echo "
+loadavg_one=${loadavg_one}
+loadavg_five=${loadavg_five}
+loadavg_fifteen=${loadavg_fifteen}"
+
+fi
+
+processes=($(top -bn1 | grep zombie | awk '{print $4" "$6" "$8" "$10}'))
+
+processes_running=${processes[0]}
+processes_sleeping=${processes[1]}
+processes_stopped=${processes[2]}
+processes_zombie=${processes[3]}
+
+if [ "$debug" == "true" ]
+then
+
+echo "
+processes_running=${processes_running}
+processes_sleeping=${processes_sleeping}
+processes_stopped=${processes_stopped}
+processes_zombie=${processes_zombie}"
+
+fi
+
+##
+## ╔╗╔┌─┐┌┬┐┌─┐┌┬┐┌─┐┌┬┐ ╦ ┌─┐┬┌─┬ ┬ ╦┌┐┌┌─┐┬ ┬ ┬─┐ ┬╔╦╗╔╗
+## ║║║├┤ │ └─┐ │ ├─┤ │ ─── ║ │ │├┴┐│ ┌┼─ ║│││├┤ │ │ │┌┴┬┘ ║║╠╩╗
+## ╝╚╝└─┘ ┴ └─┘ ┴ ┴ ┴ ┴ ╩═╝└─┘┴ ┴┴ └┘ ╩┘└┘└ ┴─┘└─┘┴ └─═╩╝╚═╝
+##
+
+influxdb_port=$(echo "$SENSE_COLLECTOR_INFLUXDB_URL" | cut -d ':' -f3 | cut -c1-4)
+#loki_port=$(echo "$SENSE_COLLECTOR_LOKI_CLIENT_URL" | cut -d ':' -f3 | cut -c1-4)
+
+#loki_netstat=$(netstat -ant | grep "${loki_port}")
+#loki_established=$(echo "${loki_netstat}" | grep -c ESTABLISHED )
+#loki_finwait2=$(echo "${loki_netstat}" | grep -c FIN_WAIT_2 )
+#loki_closewait=$(echo "${loki_netstat}" | grep -c CLOSE_WAIT )
+#loki_listen=$(echo "${loki_netstat}" | grep -c LISTENING )
+#loki_timewait=$(echo "${loki_netstat}" | grep -c TIME_WAIT )
+
+influxdb_netstat=$(netstat -ant | grep "${influxdb_port}")
+influxdb_established=$(echo "${influxdb_netstat}" | grep -c ESTABLISHED )
+influxdb_finwait2=$(echo "${influxdb_netstat}" | grep -c FIN_WAIT_2 )
+influxdb_closewait=$(echo "${influxdb_netstat}" | grep -c CLOSE_WAIT )
+influxdb_listen=$(echo "${influxdb_netstat}" | grep -c LISTENING )
+influxdb_timewait=$(echo "${influxdb_netstat}" | grep -c TIME_WAIT )
+
+if [ "$debug" == "true" ]
+then
+
+echo "loki_established=${loki_established} loki_finwait2=${loki_finwait2} loki_closewait=${loki_closewait} loki_listen=${loki_listen} loki_timewait=${loki_timewait}"
+echo "influxdb_established=${influxdb_established} influxdb_finwait2=${influxdb_finwait2} influxdb_closewait=${influxdb_closewait} influxdb_listen=${influxdb_listen} influxdb_timewait=${influxdb_timewait}"
+
+fi
+
+##
+## ╔═╗┌─┐┬─┐ ╔═╗┬─┐┌─┐┌─┐┌─┐┌─┐┌─┐ ╔═╗╔═╗╦ ╦
+## ╠═╝├┤ ├┬┘ ╠═╝├┬┘│ ││ ├┤ └─┐└─┐ ║ ╠═╝║ ║
+## ╩ └─┘┴└─ ╩ ┴└─└─┘└─┘└─┘└─┘└─┘ ╚═╝╩ ╚═╝
+##
+## Derived from https://github.com/AraKhachatryan/top
+##
+
+pid_array=$(ls /proc | grep -E '^[0-9]+$')
+clock_ticks=$(getconf CLK_TCK)
+
+for pid in $pid_array
+do
+if [ -r /proc/"$pid"/stat ]
+then
+
+stat_array=( $(sed -E 's/(\([^\s)]+)\s([^)]+\))/\1_\2/g' /proc/"$pid"/stat 2>/dev/null) )
+
+##
+## Sometimes the PID disappears before we can get some details about it. This exits the loop in that case.
+##
+
+if [ -z "$stat_array" ]; then exit 0; fi
+
+uptime_array=( $(cat /proc/uptime) )
+comm=( $(grep -Pos '^[^\s\/]+' /proc/"$pid"/comm) )
+
+if [ -z "$comm" ]; then exit 0; fi
+
+uptime=${uptime_array[0]}
+ppid=${stat_array[3]}
+utime=${stat_array[13]}
+stime=${stat_array[14]}
+cstime=${stat_array[16]}
+starttime=${stat_array[21]}
+total_time=$(( utime + stime ))
+total_time=$(( total_time + cstime ))
+seconds=$( awk 'BEGIN {print ( '"$uptime"' - ('"$starttime"' / '"$clock_ticks"') )}' )
+cpu_usage=$( awk 'BEGIN {print ( 100 * (('$total_time' / '"$clock_ticks"') / '"$seconds"') )}' )
+
+##
+## Send Per Process CPU Metrics To InfluxDB
+##
+
+curl "${curl[@]}" -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "
+sense_o11y,host_hostname=${host_hostname},source=${collector_type},sysperf_type=process_utilization,sysperf_command=${comm} pid=${pid},ppid=${ppid},uptime=${uptime},utime=${utime},stime=${stime},cstime=${cstime},starttime=${starttime},total_time=${total_time},seconds=${seconds},cpu_usage=${cpu_usage}"
+
+fi
+done
+
+##
+## Send CPU, Memory, and Netstat Metrics To InfluxDB
+##
+
+curl "${curl[@]}" -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "
+sense_o11y,host_hostname=${host_hostname},source=${collector_type},sysperf_type=cpu cpu_gnice=${cpu_gnice},cpu_guest=${cpu_guest},cpu_idle=${cpu_idle},cpu_iowait=${cpu_iowait},cpu_irq=${cpu_irq},cpu_nice=${cpu_nice},cpu_soft=${cpu_soft},cpu_steal=${cpu_steal},cpu_sys=${cpu_sys},cpu_usr=${cpu_usr}
+sense_o11y,host_hostname=${host_hostname},source=${collector_type},sysperf_type=memory mem_available=${mem_available},mem_buffers=${mem_buffers},mem_cache=${mem_cache},mem_free=${mem_free},mem_shared=${mem_shared},mem_total=${mem_total},mem_used=${mem_used},swap_free=${swap_free},swap_total=${swap_total},swap_used=${swap_used}
+sense_o11y,host_hostname=${host_hostname},source=${collector_type},sysperf_type=process_count processes_running=${processes_running},processes_sleeping=${processes_sleeping},processes_stopped=${processes_stopped},processes_zombie=${processes_zombie}
+sense_o11y,host_hostname=${host_hostname},source=${collector_type},sysperf_type=loadavg loadavg_fifteen=${loadavg_fifteen},loadavg_five=${loadavg_five},loadavg_one=${loadavg_one}
+sense_o11y,host_hostname=${host_hostname},source=${collector_type},sysperf_type=netstat,netstat_app=influxdb netstat_established=${influxdb_established},netstat_finwait2=${influxdb_finwait2},netstat_closewait=${influxdb_closewait},netstat_listen=${influxdb_listen},netstat_timewait=${influxdb_timewait}"
+
+##
+## End Observations Timer
+##
+
+observations_end=$(date +%s%N)
+observations_duration=$((observations_end-observations_start))
+
+if [ "$debug" == "true" ]; then echo "$(date) - observations_duration:${observations_duration}"; fi
+
+##
+## Send Observations Metrics To InfluxDB
+##
+
+if [ -n "$influxdb_url" ]; then
+
+curl "${curl[@]}" -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "
+sense_o11y,host_hostname=${host_hostname},function="host_performance",source=${collector_type} duration=${observations_duration}"
+
+fi
\ No newline at end of file
diff --git a/sense-collector/exec-monitor-status.sh b/sense-collector/exec-monitor-status.sh
new file mode 100644
index 0000000..e7ac054
--- /dev/null
+++ b/sense-collector/exec-monitor-status.sh
@@ -0,0 +1,284 @@
+#!/bin/bash
+
+##
+## Sense Collector - exec-monitor-status.sh
+##
+
+##
+## Sense-Collector Details
+##
+
+source sense-collector-details.sh
+
+##
+## Set Specific Variables
+##
+
+collector_type="monitor-status"
+
+##
+## Start Observations Timer
+##
+
+observations_start=$(date +%s%N)
+
+##
+## Sense-Collector Details
+##
+
+debug=$SENSE_COLLECTOR_DEBUG
+debug_curl=$SENSE_COLLECTOR_DEBUG_CURL
+debug_if=$SENSE_COLLECTOR_DEBUG_IF
+host_hostname=$SENSE_COLLECTOR_HOST_HOSTNAME
+influxdb_password=$SENSE_COLLECTOR_INFLUXDB_PASSWORD
+influxdb_url=$SENSE_COLLECTOR_INFLUXDB_URL
+influxdb_username=$SENSE_COLLECTOR_INFLUXDB_USERNAME
+sense_monitor_id=$SENSE_COLLECTOR_MONITOR_ID
+sense_token=$SENSE_COLLECTOR_TOKEN
+
+if [ "$debug" == "true" ]; then
+
+echo "debug=${debug}
+debug_curl=${debug_curl}
+debug_if=${debug_if}
+host_hostname=${host_hostname}
+influxdb_password=${influxdb_password}
+influxdb_url=${influxdb_url}
+influxdb_username=${influxdb_username}
+sense_monitor_id=${sense_monitor_id}
+sense_token=${sense_token}"
+
+fi
+
+##
+## Curl Command
+##
+
+if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --show-error --fail ); fi
+
+url_sense_get_status="https://api.sense.com/apiservice/api/v1/app/monitors/${sense_monitor_id}/status"
+
+response_url_sense_get_status=$(curl "${curl[@]}" -k -H "Sense-Collector-Client-Version: 1.0.0" -H "X-Sense-Protocol: 3" -H "User-Agent: okhttp/3.8.0" -H "Authorization: bearer ${sense_token}" "${url_sense_get_status}")
+
+#echo "${response_url_sense_get_status}"
+
+eval "$(echo "${response_url_sense_get_status}" | jq -r '.signals | {"progress", "status"}' | \
+jq -r '. | to_entries | .[] | .key + "=" + "\"" + ( .value|tostring ) + "\""')"
+
+eval "$(echo "${response_url_sense_get_status}" | jq -r '.monitor_info | {"ethernet", "test_result", "serial", "emac", "ndt_enabled", "wifi_strength", "online", "ip_address", "version", "ssid", "signal", "mac"}' | \
+jq -r '. | to_entries | .[] | .key + "=" + "\"" + ( .value|tostring ) + "\""')"
+
+if [ "$debug" == "true" ]; then
+
+echo "
+progress=${progress}
+status=${status}
+ethernet=${ethernet}
+test_result=${test_result}
+serial=${serial}
+emac=${emac}
+ndt_enabled=${ndt_enabled}
+wifi_strength=${wifi_strength}
+online=${online}
+ip_address=${ip_address}
+version=${version}
+ssid=${ssid}
+signal=${signal}
+mac=${mac}
+"
+
+fi
+
+curl_message="sense_monitor_status,serial=${serial} "
+
+if [ "${progress}" != "null" ]; then curl_message="${curl_message}progress=${progress},"; else if [ "$debug_if" == "true" ]; then echo "progress is null"; fi; fi
+if [ "${status}" != "null" ]; then curl_message="${curl_message}status=\"${status}\","; else if [ "$debug_if" == "true" ]; then echo "status is null"; fi; fi
+if [ "${ethernet}" != "null" ]; then curl_message="${curl_message}ethernet=\"${ethernet}\","; else if [ "$debug_if" == "true" ]; then echo "ethernet is null"; fi; fi
+if [ "${test_result}" != "null" ]; then curl_message="${curl_message}test_result=\"${test_result}\","; else if [ "$debug_if" == "true" ]; then echo "test_result is null"; fi; fi
+if [ "${emac}" != "null" ]; then curl_message="${curl_message}emac=\"${emac}\","; else if [ "$debug_if" == "true" ]; then echo "emac is null"; fi; fi
+if [ "${ndt_enabled}" != "null" ]; then curl_message="${curl_message}ndt_enabled=\"${ndt_enabled}\","; else if [ "$debug_if" == "true" ]; then echo "ndt_enabled is null"; fi; fi
+if [ "${wifi_strength}" != "null" ]; then curl_message="${curl_message}wifi_strength=-${wifi_strength},"; else if [ "$debug_if" == "true" ]; then echo "wifi_strength is null"; fi; fi
+if [ "${online}" != "null" ]; then curl_message="${curl_message}online=\"${online}\","; else if [ "$debug_if" == "true" ]; then echo "online is null"; fi; fi
+if [ "${ip_address}" != "null" ]; then curl_message="${curl_message}ip_address=\"${ip_address}\","; else if [ "$debug_if" == "true" ]; then echo "ip_address is null"; fi; fi
+if [ "${version}" != "null" ]; then curl_message="${curl_message}version=\"${version}\","; else if [ "$debug_if" == "true" ]; then echo "version is null"; fi; fi
+if [ "${ssid}" != "null" ]; then curl_message="${curl_message}ssid=\"${ssid}\","; else if [ "$debug_if" == "true" ]; then echo "ssid is null"; fi; fi
+if [ "${signal}" != "null" ]; then curl_message="${curl_message}signal=\"${signal}\","; else if [ "$debug_if" == "true" ]; then echo "signal is null"; fi; fi
+if [ "${mac}" != "null" ]; then curl_message="${curl_message}mac=\"${mac}\","; else if [ "$debug_if" == "true" ]; then echo "mac is null"; fi; fi
+
+##
+## Remove a trailing comma in curl_message if the last element happens to be null (so that there's still a properly formatted InfluxDB mmessage)
+##
+
+curl_message="$(echo "${curl_message}" | sed 's/,$//')"
+
+if [ "$debug" == "true" ]; then
+
+echo "${curl_message}"
+
+fi
+
+if [ "$curl_debug" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
+
+/usr/bin/timeout -k 1 10s curl "${curl[@]}" --connect-timeout 2 --max-time 2 --retry 5 --retry-delay 0 --retry-max-time 30 -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "${curl_message}" &
+
+##
+## Device Detection - In Progress
+##
+
+number_of_devices_in_progress=$(echo "${response_url_sense_get_status}" | jq '.device_detection.in_progress | length')
+
+number_of_devices_in_progress_minus_one=$((number_of_devices_in_progress-1))
+
+if [ "$debug" == "true" ]; then
+
+echo "number_of_devices_in_progress=${number_of_devices_in_progress}"
+
+fi
+
+for device_number in $(seq 0 $number_of_devices_in_progress_minus_one) ; do
+
+eval "$(echo "${response_url_sense_get_status}" | jq -r '.device_detection.in_progress['"$device_number"'] | {"icon", "name", "progress"}' | \
+jq -r '. | to_entries | .[] | .key + "=" + "\"" + ( .value|tostring ) + "\""')"
+
+if [ "$debug" == "true" ]; then
+
+echo "Device Detection - In Progress"
+
+echo "
+icon=${icon}
+name=${name}
+progress=${progress}"
+
+fi
+
+##
+## Escape Names (Function)
+##
+
+escape_names
+
+curl_message="sense_monitor_device_detection,detection_type=in_progress,name=${name_escaped},serial=${serial} "
+
+if [ "${icon}" != "null" ]; then curl_message="${curl_message}icon=\"${icon}\","; else if [ "$debug_if" == "true" ]; then echo "icon is null"; fi; fi
+if [ "${progress}" != "null" ]; then curl_message="${curl_message}progress=${progress},"; else if [ "$debug_if" == "true" ]; then echo "progress is null"; fi; fi
+
+##
+## Remove a trailing comma in curl_message if the last element happens to be null (so that there's still a properly formatted InfluxDB mmessage)
+##
+
+curl_message="$(echo "${curl_message}" | sed 's/,$//')"
+
+if [ "$debug" == "true" ]; then
+
+echo "${curl_message}"
+
+fi
+
+if [ "$curl_debug" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
+
+/usr/bin/timeout -k 1 10s curl "${curl[@]}" --connect-timeout 2 --max-time 2 --retry 5 --retry-delay 0 --retry-max-time 30 -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "${curl_message}" &
+
+done
+
+##
+## Device Detection - Found
+##
+
+number_of_devices_found=$(echo "${response_url_sense_get_status}" | jq '.device_detection.found | length')
+
+number_of_devices_found_minus_one=$((number_of_devices_found-1))
+
+if [ "$debug" == "true" ]; then
+
+echo "number_of_devices_found=${number_of_devices_found}"
+
+fi
+
+for device_number in $(seq 0 $number_of_devices_found_minus_one) ; do
+
+eval "$(echo "${response_url_sense_get_status}" | jq -r '.device_detection.found['"$device_number"'] | to_entries | .[] | .key + "=" + "\"" + ( .value|tostring ) + "\""')"
+
+if [ "$debug" == "true" ]; then
+
+echo "Device Detection - Found"
+
+echo "
+icon=${icon}
+name=${name}
+progress=${progress}"
+fi
+
+##
+## Escape Names (Function)
+##
+
+escape_names
+
+curl_message="sense_monitor_device_detection,detection_type=found,name=${name_escaped},serial=${serial} "
+
+if [ "${icon}" != "null" ]; then curl_message="${curl_message}icon=\"${icon}\","; else if [ "$debug_if" == "true" ]; then echo "icon is null"; fi; fi
+if [ "${progress}" != "null" ]; then curl_message="${curl_message}progress=${progress},"; else if [ "$debug_if" == "true" ]; then echo "progress is null"; fi; fi
+
+##
+## Remove a trailing comma in curl_message if the last element happens to be null (so that there's still a properly formatted InfluxDB mmessage)
+##
+
+curl_message="$(echo "${curl_message}" | sed 's/,$//')"
+
+if [ "$debug" == "true" ]; then
+
+echo "${curl_message}"
+
+fi
+
+if [ "$curl_debug" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
+
+/usr/bin/timeout -k 1 10s curl "${curl[@]}" --connect-timeout 2 --max-time 2 --retry 5 --retry-delay 0 --retry-max-time 30 -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "${curl_message}" &
+
+done
+
+##
+## Device Detection - Number Detected
+##
+
+num_detected=$(echo "${response_url_sense_get_status}" | jq -r '.device_detection.num_detected')
+
+if [ "$debug" == "true" ]; then
+
+echo "Device Detection - Number Detected"
+echo "num_detected=${num_detected}"
+fi
+
+curl_message="sense_monitor_device_detection,serial=${serial} num_detected=${num_detected}"
+
+if [ "$debug" == "true" ]; then
+
+echo "${curl_message}"
+
+fi
+
+if [ "$curl_debug" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
+
+/usr/bin/timeout -k 1 10s curl "${curl[@]}" --connect-timeout 2 --max-time 2 --retry 5 --retry-delay 0 --retry-max-time 30 -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "${curl_message}" &
+
+wait
+
+##
+## End Observations Timer
+##
+
+observations_end=$(date +%s%N)
+observations_duration=$((observations_end-observations_start))
+
+if [ "$debug" == "true" ]; then echo "$(date) - observations_duration:${observations_duration}"; fi
+
+##
+## Send Observations Metrics To InfluxDB
+##
+
+if [ -n "$influxdb_url" ]; then
+
+curl "${curl[@]}" -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "
+sense_o11y,host_hostname=${host_hostname},function="monitor_status",source=${collector_type} duration=${observations_duration}"
+
+fi
\ No newline at end of file
diff --git a/sense-collector/exec-sense-collector.sh b/sense-collector/exec-sense-collector.sh
index 58b3909..cd18f40 100644
--- a/sense-collector/exec-sense-collector.sh
+++ b/sense-collector/exec-sense-collector.sh
@@ -1,39 +1,71 @@
#!/bin/bash
##
-## Sense Collector - exec-sense-collector.sh
+## Sense Collector - start-sense-collector.sh
##
+##
+## Set Specific Variables
+##
+
+collector_type="sense-collector"
+
+##
+## Sense-Collector Details
+##
+
+source sense-collector-details.sh
+
##
## Sense-Collector Details
##
debug=$SENSE_COLLECTOR_DEBUG
debug_curl=$SENSE_COLLECTOR_DEBUG_CURL
+debug_if=$SENSE_COLLECTOR_DEBUG_IF
+host_hostname=$SENSE_COLLECTOR_HOST_HOSTNAME
influxdb_password=$SENSE_COLLECTOR_INFLUXDB_PASSWORD
influxdb_url=$SENSE_COLLECTOR_INFLUXDB_URL
influxdb_username=$SENSE_COLLECTOR_INFLUXDB_USERNAME
+poll_interval=$SENSE_COLLECTOR_SENSE_COLLECTOR_POLL_INTERVAL
+sense_monitor_id=$SENSE_COLLECTOR_MONITOR_ID
+sense_token=$SENSE_COLLECTOR_TOKEN
+threads=$SENSE_COLLECTOR_THREADS
+
+##
+## Set Threads
+##
+
+if [ -z "${threads}" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_THREADS${echo_normal} environmental variable not set. Defaulting to ${echo_bold}4${echo_normal} threads."; threads="4"; export SENSE_COLLECTOR_THREADS="4"; fi
if [ "$debug" == "true" ]; then
echo "debug=${debug}
debug_curl=${debug_curl}
+debug_if=${debug_if}
+host_hostname=${host_hostname}
influxdb_password=${influxdb_password}
influxdb_url=${influxdb_url}
-influxdb_username=${influxdb_username}"
+influxdb_username=${influxdb_username}
+poll_interval=${poll_interval}
+sense_monitor_id=${sense_monitor_id}
+sense_token=${sense_token}
+threads=${threads}"
+
fi
##
-## Example Command
+## Set New Line Variable
##
-# ./start.sh | SENSE_COLLECTOR_INFLUXDB_PASSWORD=none SENSE_COLLECTOR_INFLUXDB_USERNAME=none SENSE_COLLECTOR_INFLUXDB_URL=http://influxdb01.tylephony.com:8086/write?db=sense SENSE_COLLECTOR_DEBUG=true ./exec-sense-collector.sh
+NL=$'\n'
##
-## Curl Command
+## Testing
##
-if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
+#debug=true
+#debug_curl=true
##
## Read Socket
@@ -41,13 +73,82 @@ if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --output /d
while read -r line; do
+if [ "$debug" == "true" ]
+then
+
+echo ""
+echo "${line}"
+echo ""
+
+fi
+
##
-## Get non-device metrics
+## Get Power Mains Metrics
+##
+
+##
+## ┬─┐┌─┐┌─┐┬ ┌┬┐┬┌┬┐┌─┐ ┬ ┬┌─┐┌┬┐┌─┐┌┬┐┌─┐
+## ├┬┘├┤ ├─┤│ │ ││││├┤ │ │├─┘ ││├─┤ │ ├┤
+## ┴└─└─┘┴ ┴┴─┘┴ ┴┴ ┴└─┘────└─┘┴ ─┴┘┴ ┴ ┴ └─┘
##
if [[ $line == *"realtime_update"* ]]; then
-eval "$(echo "${line}" | jq -r '.payload | to_entries | .[6,7,8,10,11] | .key + "=" + "\"" + ( .value|tostring ) + "\""')"
+
+##
+## Process realtime_update by streaming or on a polling interval
+##
+
+poll_check=$(date +"%S")
+
+if [ "$debug" == "true" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} poll_check=${poll_check}"; fi
+
+if [ "$poll_interval" == "5" ]; then
+
+if [ "$poll_check" == "00" ] || [ "$poll_check" == "05" ] || [ "$poll_check" == "10" ] || [ "$poll_check" == "15" ] || [ "$poll_check" == "20" ] || \
+[ "$poll_check" == "25" ] || [ "$poll_check" == "30" ] || [ "$poll_check" == "35" ] || [ "$poll_check" == "40" ] || [ "$poll_check" == "45" ] || \
+[ "$poll_check" == "50" ] || [ "$poll_check" == "55" ]; then run_collector="true"; if [ "$debug_sleeping" == "true" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} Running Collector Poll - ${echo_bold}5${echo_normal} seconds."; fi; else continue; fi
+
+fi
+
+if [ "$poll_interval" == "10" ]; then
+
+if [ "$poll_check" == "00" ] || [ "$poll_check" == "10" ] || [ "$poll_check" == "20" ] || [ "$poll_check" == "30" ] || [ "$poll_check" == "40" ] || \
+[ "$poll_check" == "50" ]; then run_collector="true"; if [ "$debug_sleeping" == "true" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} Running Collector Poll - ${echo_bold}10${echo_normal} seconds."; fi; else continue; fi
+
+fi
+
+if [ "$poll_interval" == "15" ]; then
+
+if [ "$poll_check" == "00" ] || [ "$poll_check" == "15" ] || [ "$poll_check" == "30" ] || [ "$poll_check" == "45" ]; then run_collector="true"; if [ "$debug_sleeping" == "true" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} Running Collector Poll - ${echo_bold}15${echo_normal} seconds."; fi; else continue; fi
+
+fi
+
+if [ "$poll_interval" == "30" ]; then
+
+if [ "$poll_check" == "00" ] || [ "$poll_check" == "30" ]; then run_collector="true"; if [ "$debug_sleeping" == "true" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} Running Collector Poll - ${echo_bold}30${echo_normal} seconds."; fi; else continue; fi
+
+fi
+
+if [ "$poll_interval" == "60" ]; then
+
+if [ "$poll_check" == "00" ]; then run_collector="true"; if [ "$debug_sleeping" == "true" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} Running Collector Poll - ${echo_bold}60${echo_normal} seconds."; fi; else continue; fi
+
+fi
+
+##
+## Start Observations Timer
+##
+
+observations_start=$(date +%s%N)
+
+##
+## ╔╦╗┌─┐┬┌┐┌┌─┐
+## ║║║├─┤││││└─┐
+## ╩ ╩┴ ┴┴┘└┘└─┘
+##
+
+eval "$(echo "${line}" | jq -r '.payload | {"hz", "w", "c", "d_w", "epoch"} | to_entries | .[] | .key + "=" + "\"" + ( .value|tostring ) + "\""')"
voltage=($(echo "${line}" | jq -r '.payload.voltage | @sh') )
watts=($(echo "${line}" | jq -r '.payload.channels | @sh') )
@@ -59,19 +160,25 @@ voltage=${voltage[0]}, ${voltage[1]}
watts=${watts[0]}, ${watts[1]}
hz=${hz}
w=${w}
-c=${c}"
+c=${c}
+epoch=${epoch}"
+echo "epoch top: ${epoch}"
fi
-curl_message=""
-NL=$'\n'
+##
+## Clear Curl Message
+##
-if [ "${voltage[0]}" != "null" ]; then curl_message="${curl_message}sense_mains,leg=L1 voltage=${voltage[0]}${NL}"; else echo "leg=L1 voltage is null"; fi
-if [ "${voltage[1]}" != "null" ]; then curl_message="${curl_message}sense_mains,leg=L2 voltage=${voltage[1]}${NL}"; else echo "leg=L2 voltage is null"; fi
-if [ "${watts[0]}" != "null" ]; then curl_message="${curl_message}sense_mains,leg=L1 watts=${watts[0]}${NL}"; else echo "leg=L1 watts is null"; fi
-if [ "${watts[1]}" != "null" ]; then curl_message="${curl_message}sense_mains,leg=L2 watts=${watts[1]}${NL}"; else echo "leg=L2 watts is null"; fi
-if [ "${hz}" != "null" ]; then curl_message="${curl_message}sense_mains hz=${hz}${NL}"; else echo "hz is null"; fi
-if [ "${c}" != "null" ]; then curl_message="${curl_message}sense_mains c=${c}${NL}"; else echo "c is null"; fi
+if [ "${epoch}" != "null" ]; then curl_message=""; else
+if [ "$debug_if" == "true" ]; then echo "epoch is null. Skipping realtime_update"; fi; continue; fi
+
+if [ "${voltage[0]}" != "null" ]; then curl_message="${curl_message}sense_mains,leg=L1 voltage=${voltage[0]} ${epoch}${NL}"; else if [ "$debug_if" == "true" ]; then echo "leg=L1 voltage is ${voltage[0]}."; fi; continue; fi
+if [ "${voltage[1]}" != "null" ]; then curl_message="${curl_message}sense_mains,leg=L2 voltage=${voltage[1]} ${epoch}${NL}"; else if [ "$debug_if" == "true" ]; then echo "leg=L2 voltage is ${voltage[1]}."; fi; continue; fi
+if [ "${watts[0]}" != "null" ]; then curl_message="${curl_message}sense_mains,leg=L1 watts=${watts[0]} ${epoch}${NL}"; else if [ "$debug_if" == "true" ]; then echo "leg=L1 watts is ${watts[0]}."; fi; continue; fi
+if [ "${watts[1]}" != "null" ]; then curl_message="${curl_message}sense_mains,leg=L2 watts=${watts[1]} ${epoch}${NL}"; else if [ "$debug_if" == "true" ]; then echo "leg=L2 watts is ${watts[1]}."; fi; continue; fi
+if [ "${hz}" != "null" ]; then curl_message="${curl_message}sense_mains hz=${hz} ${epoch}${NL}"; else if [ "$debug_if" == "true" ]; then echo "hz is ${hz}."; fi; continue; fi
+if [ "${c}" != "null" ]; then curl_message="${curl_message}sense_mains c=${c} ${epoch}${NL}"; else if [ "$debug_if" == "true" ]; then echo "c is ${c}."; fi; continue; fi
##
## Remove a new line in curl_message if the last element happens to be null (so that there's still a properly formatted InfluxDB mmessage)
@@ -79,7 +186,65 @@ if [ "${c}" != "null" ]; then curl_message="${curl_message}sense_mains c=${c}${N
curl_message="$(echo "${curl_message}" | sed 's/\r$//')"
-/usr/bin/timeout -k 1 2s curl "${curl[@]}" -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "${curl_message}" &
+if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
+
+if [ "$debug" == "true" ]; then echo "${curl_message}"; fi
+
+##
+## Set InfluxDB Precision to Seconds to use the epoch time from Sense
+##
+
+##
+## Curl Command
+##
+
+if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
+
+/usr/bin/timeout -k 1 10s curl "${curl[@]}" --connect-timeout 2 --max-time 2 --retry 5 --retry-delay 0 --retry-max-time 30 -i -XPOST "${influxdb_url}&precision=s" -u "${influxdb_username}":"${influxdb_password}" --data-binary "${curl_message}" &
+
+##
+## End Observations Timer
+##
+
+observations_end=$(date +%s%N)
+observations_duration=$((observations_end-observations_start))
+
+if [ "$debug" == "true" ]; then echo "$(date) - observations_duration:${observations_duration}"; fi
+
+##
+## Send Observations Metrics To InfluxDB
+##
+
+if [ -n "$influxdb_url" ]; then
+
+curl "${curl[@]}" -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "
+sense_o11y,host_hostname=${host_hostname},function="realtime_update_mains",source=${collector_type} duration=${observations_duration}"
+
+fi
+
+## Mac
+#curl "${curl[@]}" --connect-timeout 2 --max-time 2 --retry 5 --retry-delay 0 --retry-max-time 30 -i -XPOST "${influxdb_url}&precision=s" -u "${influxdb_username}":"${influxdb_password}" --data-binary "${curl_message}" &
+
+#server_epoch=$(date +%s)
+#difference_epoch=$((server_epoch-epoch))
+
+#echo "MAINS: server_epoch=${server_epoch} sense_epoch=${epoch} difference_epoch=${difference_epoch}"
+
+#continue;
+
+##
+## ╔╦╗┌─┐┬ ┬┬┌─┐┌─┐┌─┐
+## ║║├┤ └┐┌┘││ ├┤ └─┐
+## ═╩╝└─┘ └┘ ┴└─┘└─┘└─┘
+##
+
+
+##
+## Start Observations Timer
+##
+
+observations_start=$(date +%s%N)
+
##
## Number of devices
@@ -88,70 +253,187 @@ curl_message="$(echo "${curl_message}" | sed 's/\r$//')"
num_of_devices=$(echo "${line}" | jq -r '.payload.devices | length')
num_of_devices_minus_one=$((num_of_devices-1))
+##
+## Clear variables from prior loop
+##
+
curl_message=""
-NL=$'\n'
+sd="null"
+i="null"
+v="null"
+e="null"
+
+##
+## Start "threading"
+##
for device in $(seq 0 $num_of_devices_minus_one); do
-eval "$(echo "${line}" | jq -r '.payload.devices['"${device}"'] | to_entries | .[0,1,5] | .key + "=" + "\"" + ( .value|tostring ) + "\""')"
+(
+
+eval "$(echo "${line}" | jq -r '.payload.devices['"${device}"'] | {"id", "name", "w", "sd", "ao_w"} | to_entries | .[] | .key + "=" + "\"" + ( .value|tostring ) + "\""')"
+
+##
+## Check the ${sd} variable to see if here are additional details for discovered plugs. If found - we'll overwrite the ${w} value since it should be the same.
+##
+
+if [ "${sd}" != "null" ]; then if [ "$debug" == "true" ]; then echo "name=${name}, sd=${sd}"; fi; eval "$(echo "${line}" | jq -r '.payload.devices['"${device}"'].sd | {"w", "i", "v", "e"} | to_entries | .[] | .key + "=" + "\"" + ( .value|tostring ) + "\""')"; fi
+
+curl_message="${curl_message}sense_devices,"
+
+if [ "${id}" != "null" ]; then curl_message="${curl_message}id=${id},"; else echo "id is null. Skipping device."; continue; fi
##
## Escape Names (Function)
##
+if [ "${name}" != "null" ]; then escape_names; curl_message="${curl_message}name=${name_escaped},"; else if [ "$debug_if" == "true" ]; then echo "${name} - id is null. Skipping device."; continue; fi; fi
+
+if [ "${sd}" != "null" ]; then curl_message="${curl_message}is_plug=true,"; else curl_message="${curl_message}is_plug=false,"; fi
+if [ "${ao_w}" != "null" ]; then curl_message="${curl_message}always_on=true,"; else curl_message="${curl_message}always_on=false,"; fi
+
##
-## Functions
+## Remove a trailing comma and add a space in curl_message
##
+curl_message="$(echo "${curl_message}" | sed 's/,$/ /')"
+
+if [ "${i}" != "null" ]; then curl_message="${curl_message}i=${i},"; else if [ "$debug_if" == "true" ]; then echo "${name} - i is null."; fi; fi
+if [ "${v}" != "null" ]; then curl_message="${curl_message}v=${v},"; else if [ "$debug_if" == "true" ]; then echo "${name} - v is null."; fi; fi
+if [ "${e}" != "null" ]; then curl_message="${curl_message}e=${e},"; else if [ "$debug_if" == "true" ]; then echo "${name} - e is null."; fi; fi
+if [ "${ao_w}" != "null" ]; then curl_message="${curl_message}ao_w=${ao_w},"; else if [ "$debug_if" == "true" ]; then echo "${name} - ao_w is null."; fi; fi
+
+if [ "${w}" != "null" ]; then curl_message="${curl_message}current_watts=${w}"; else if [ "$debug_if" == "true" ]; then echo "${name} - w is null. Skipping device."; fi; continue; fi
+
##
-## ┌─┐┌─┐┌─┐┌─┐┌─┐┌─┐ ┌┐┌┌─┐┌┬┐┌─┐┌─┐
-## ├┤ └─┐│ ├─┤├─┘├┤ │││├─┤│││├┤ └─┐
-## └─┘└─┘└─┘┴ ┴┴ └─┘────┘└┘┴ ┴┴ ┴└─┘└─┘
+## Add Epoch Time
##
-#function escape_names () {
+curl_message="${curl_message} ${epoch}"
+
+if [ "$debug" == "true" ]; then echo "device=${device}, device_id=${id}, name_escaped=${name_escaped}, current_watts=${w}, i=${i}, v=${v}, e=${e}"; fi
##
-## Spaces
+## Remove a trailing comma in curl_message if the last element happens to be null (so that there's still a properly formatted InfluxDB mmessage)
##
-name_escaped="${name// /\\ }"
+#curl_message="$(echo "${curl_message}" | sed 's/,$//')"
+
+#echo "curl_message=${curl_message}"
+
+#curl_message="$(echo "${curl_message}" | sed 's/\r$//')"
+
+if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
##
-## Commas
+## Set InfluxDB Precision to Seconds to use the epoch time from Sense
##
-name_escaped="${name_escaped//,/\\,}"
+/usr/bin/timeout -k 1 10s curl "${curl[@]}" --connect-timeout 2 --max-time 2 --retry 5 --retry-delay 0 --retry-max-time 30 -i -XPOST "${influxdb_url}&precision=s" -u "${influxdb_username}":"${influxdb_password}" --data-binary "${curl_message}" &
##
-## Equal Signs
+## Clear ${sd}
##
-name_escaped="${name_escaped//=/\\=}"
+sd="null"
+i="null"
+v="null"
+e="null"
-#}
+## Mac
+#curl "${curl[@]}" --connect-timeout 2 --max-time 2 --retry 5 --retry-delay 0 --retry-max-time 30 -i -XPOST "${influxdb_url}&precision=s" -u "${influxdb_username}":"${influxdb_password}" --data-binary "${curl_message}" &
-if [ "$debug" == "true" ]; then echo "device=${device}, id=${id}, name_escaped=${name_escaped}, watts=${w}"; fi
+) &
-curl_message="${curl_message}sense_devices,id=${id},name=${name_escaped} watts=${w}${NL}"
+if [[ $(jobs -r -p | wc -l) -ge $threads ]]; then wait -n; fi
done
+wait
+#echo "curl_message=${curl_message}"
+
+#echo "Device Loop Finished"
+
+#if [ "$debug" == "true" ]; then echo "Device Loop Finished"; fi
+
+##
+## End "threading"
+##
+
##
## Remove a new line in curl_message if the last element happens to be null (so that there's still a properly formatted InfluxDB mmessage)
##
curl_message="$(echo "${curl_message}" | sed 's/\r$//')"
-#echo "${curl_message}"
+if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
+
+##
+## Set InfluxDB Precision to Seconds to use the epoch time from Sense
+##
+
+/usr/bin/timeout -k 1 10s curl "${curl[@]}" --connect-timeout 2 --max-time 2 --retry 5 --retry-delay 0 --retry-max-time 30 -i -XPOST "${influxdb_url}&precision=s" -u "${influxdb_username}":"${influxdb_password}" --data-binary "${curl_message}" &
+
+#curl "${curl[@]}" --connect-timeout 2 --max-time 2 --retry 5 --retry-delay 0 --retry-max-time 30 -i -XPOST "${influxdb_url}&precision=s" -u "${influxdb_username}":"${influxdb_password}" --data-binary "${curl_message}" &
+
+##
+## Send Epoch Drift Metrics To InfluxDB
+##
+
+server_epoch=$(date +%s)
+difference_epoch=$((server_epoch-epoch))
+
+if [ -n "$influxdb_url" ]; then
-/usr/bin/timeout -k 1 2s curl "${curl[@]}" -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "${curl_message}" &
+curl "${curl[@]}" -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "
+sense_o11y,host_hostname=${host_hostname},function="realtime_update_devices",source=${collector_type} difference_epoch=${difference_epoch}"
fi
+##
+## End Observations Timer
+##
+
+observations_end=$(date +%s%N)
+observations_duration=$((observations_end-observations_start))
+
+if [ "$debug" == "true" ]; then echo "$(date) - observations_duration:${observations_duration}"; fi
+
+##
+## Send Observations Metrics To InfluxDB
+##
+
+if [ -n "$influxdb_url" ]; then
+
+curl "${curl[@]}" -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "
+
+sense_o11y,host_hostname=${host_hostname},function="realtime_update_devices",source=${collector_type} duration=${observations_duration}"
+
+fi
+
+fi
+
+##
+## ┌┐┌┌─┐┬ ┬ ┌┬┐┬┌┬┐┌─┐┬ ┬┌┐┌┌─┐ ┌─┐┬ ┬┌─┐┌┐┌┌┬┐
+## │││├┤ │││ │ ││││├┤ │ ││││├┤ ├┤ └┐┌┘├┤ │││ │
+## ┘└┘└─┘└┴┘────┴ ┴┴ ┴└─┘┴─┘┴┘└┘└─┘────└─┘ └┘ └─┘┘└┘ ┴
+##
+
if [[ $line == *"new_timeline_event"* ]]; then
-eval "$(echo "${line}" | jq -r '.payload.items_added[] | to_entries | .[] | .key + "=" + "\"" + ( .value|tostring ) + "\""')"
+##
+## Start Observations Timer
+##
+
+observations_start=$(date +%s%N)
+
+##
+## Curl Command
+##
+
+if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
+
+eval "$(echo "${line}" | jq -r '.payload.items_added[] | {"time", "type", "icon", "body", "device_id", "device_state", "user_device_type", "device_transition_from_state"} | to_entries | .[] | .key + "=" + "\"" + ( .value|tostring ) + "\""')"
if [ "$debug" == "true" ]; then
@@ -166,22 +448,128 @@ user_device_type=${user_device_type}
device_transition_from_state=${device_transition_from_state}"
fi
-time_epoch_ns=$(date -d"${time}" +%s%N )
-time_epoch=$(date +%s%3N )
+if [ "${device_id}" != "null" ]; then url_sense_device="https://api.sense.com/apiservice/api/v1/app/monitors/${sense_monitor_id}/devices/${device_id}"; else if [ "$debug_if" == "true" ]; then echo "device_id is null. Skipping device."; fi; continue; fi
+
+if [ "$debug" == "true" ]; then echo "url_sense_device=${url_sense_device}"; fi
+
+##
+## Curl Command
+##
+
+if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --show-error --fail ); fi
+
+response_url_sense_device=$(curl "${curl[@]}" -k -H "Sense-Collector-Client-Version: 1.0.0" -H "X-Sense-Protocol: 3" -H "User-Agent: okhttp/3.8.0" -H "Authorization: bearer ${sense_token}" "${url_sense_device}")
+
+if [ "$debug" == "true" ]; then echo "response_url_sense_device=${response_url_sense_device}"; fi
-if [ "$debug" == "true" ]; then echo "sense_event,event_type=new_timeline,device_id=${device_id} type=\"${type}\",icon=\"${icon}\",body=\"${body}\",device_state=\"${device_state}\",user_device_type=\"${user_device_type}\",device_transition_from_state=\"${device_transition_from_state}\",time_epoch=${time_epoch} ${time_epoch_ns}"; fi
+name=$(echo "${response_url_sense_device}" | jq -r '.device.name')
-/usr/bin/timeout -k 1 2s curl "${curl[@]}" -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "sense_event,device_id=${device_id},event_type=new_timeline type=\"${type}\",icon=\"${icon}\",body=\"${body}\",device_state=\"${device_state}\",user_device_type=\"${user_device_type}\",device_transition_from_state=\"${device_transition_from_state}\",time_epoch=${time_epoch} ${time_epoch_ns}" &
+curl_message="sense_event,"
+
+if [ "${device_id}" != "null" ]; then curl_message="device_id=${device_id},"; else echo "device_id is null. Skipping device."; continue; fi
+
+curl_message="${curl_message}event_type=new_timeline,"
+
+##
+## Escape Names (Function)
+##
+
+if [ "${name}" != "null" ]; then escape_names; curl_message="name=${name_escaped} "; else echo "name is null. Skipping device."; continue; fi
+
+if [ "${time}" != "null" ]; then time_epoch_ns=$(date -d"${time}" +%s%N ); time_epoch=$(date -d"${time}" +%s%3N ); curl_message="${curl_message}time=${time},"; else if [ "$debug_if" == "true" ]; then echo "time is null. Skipping device."; fi; continue; fi
+if [ "${type}" != "null" ]; then curl_message="${curl_message}type=\"${type}\","; else if [ "$debug_if" == "true" ]; then echo "type is null. Skipping device."; fi; continue; fi
+if [ "${icon}" != "null" ]; then curl_message="${curl_message}icon=\"${icon}\","; else if [ "$debug_if" == "true" ]; then echo "icon is null. Skipping device."; fi; continue; fi
+if [ "${body}" != "null" ]; then curl_message="${curl_message}body=\"${body}\","; else if [ "$debug_if" == "true" ]; then echo "body is null. Skipping device."; fi; continue; fi
+
+if [ "${device_state}" != "null" ]; then curl_message="${curl_message}device_state=${device_state},"; else if [ "$debug_if" == "true" ]; then echo "device_state is null. Skipping device."; fi; continue; fi
+if [ "${user_device_type}" != "null" ]; then curl_message="${curl_message}user_device_type=${user_device_type},"; else if [ "$debug_if" == "true" ]; then echo "user_device_type is null. Skipping device."; fi; continue; fi
+if [ "${device_transition_from_state}" != "null" ]; then curl_message="${curl_message}device_transition_from_state=${device_transition_from_state},"; else if [ "$debug_if" == "true" ]; then echo "device_transition_from_state is null. Skipping device."; fi; continue; fi
+if [ "${time_epoch}" != "null" ]; then curl_message="${curl_message}time_epoch=${time_epoch},"; else if [ "$debug_if" == "true" ]; then echo "time_epoch is null. Skipping device."; fi; continue; fi
+
+##
+## Remove a comma in curl_message if the last element happens to be null (so that there's still a properly formatted InfluxDB mmessage)
+##
+
+curl_message="$(echo "${curl_message}" | sed 's/,$//')"
+curl_message="${curl_message} ${time_epoch_ns}"
+
+curl_message="sense_event,device_id=${device_id},event_type=new_timeline,name=${name_escaped} type=\"${type}\",icon=\"${icon}\",body=\"${body}\",device_state=\"${device_state}\",user_device_type=\"${user_device_type}\",device_transition_from_state=\"${device_transition_from_state}\",time_epoch=${time_epoch} ${time_epoch_ns}"
+
+##
+## Curl Command
+##
+
+if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
+
+/usr/bin/timeout -k 1 10s curl "${curl[@]}" --connect-timeout 2 --max-time 2 --retry 5 --retry-delay 0 --retry-max-time 30 -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "${curl_message}" &
+
+##
+## End Observations Timer
+##
+
+observations_end=$(date +%s%N)
+observations_duration=$((observations_end-observations_start))
+
+if [ "$debug" == "true" ]; then echo "$(date) - observations_duration:${observations_duration}"; fi
+
+##
+## Send Timer Metrics To InfluxDB
+##
+
+if [ -n "$influxdb_url" ]; then
+
+curl "${curl[@]}" -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "
+sense_o11y,host_hostname=${host_hostname},function="new_timeline_event",source=${collector_type} duration=${observations_duration}"
+
+fi
fi
+##
+## ┬ ┬┌─┐┬ ┬ ┌─┐
+## ├─┤├┤ │ │ │ │
+## ┴ ┴└─┘┴─┘┴─┘└─┘
+##
+
if [[ $line == *"hello"* ]]; then
-time_epoch=$(date +%s%3N )
+##
+## Start Observations Timer
+##
+
+observations_start=$(date +%s%N)
+
+##
+## Curl Command
+##
+
+if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
+
+time_epoch=$(date +%s%3N)
if [ "$debug" == "true" ]; then echo "sense_event,event_type=hello time_epoch=${time_epoch}"; fi
-/usr/bin/timeout -k 1 2s curl "${curl[@]}" -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "sense_event,event_type=hello time_epoch=${time_epoch}" &
+/usr/bin/timeout -k 1 10s curl "${curl[@]}" --connect-timeout 2 --max-time 2 --retry 5 --retry-delay 0 --retry-max-time 30 -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "sense_event,event_type=hello time_epoch=${time_epoch}" &
+
+##
+## End Observations Timer
+##
+
+observations_end=$(date +%s%N)
+observations_duration=$((observations_end-observations_start))
+
+if [ "$debug" == "true" ]; then echo "$(date) - observations_duration:${observations_duration}"; fi
+
+##
+## Send Timer Metrics To InfluxDB
+##
+
+if [ -n "$influxdb_url" ]; then
+
+curl "${curl[@]}" -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "
+sense_o11y,host_hostname=${host_hostname},function="hello",source=${collector_type} duration=${observations_duration}"
+
+fi
fi
diff --git a/sense-collector/sense-collector-details.sh b/sense-collector/sense-collector-details.sh
new file mode 100644
index 0000000..fcfe0ba
--- /dev/null
+++ b/sense-collector/sense-collector-details.sh
@@ -0,0 +1,95 @@
+#!/bin/bash
+
+##
+## Sense Collector - sense-collector-details.sh
+##
+
+sense_collector_version="1.0.1"
+#grafana_loki_binary_path="./promtail-linux-amd64"
+grafana_loki_binary_path="/usr/bin/promtail"
+debug_sleeping=$SENSE_COLLECTOR_DEBUG_SLEEPING
+
+##
+## Echo Details
+##
+
+echo_bold=$(tput -T xterm bold)
+echo_blink=$(tput -T xterm blink)
+echo_black=$(tput -T xterm setaf 0)
+echo_blue=$(tput -T xterm setaf 4)
+echo_dim=$(tput -T xterm dim)
+
+echo_color_random=$(echo -e "\e[3$(( $RANDOM * 6 / 32767 + 1 ))m")
+
+echo_normal=$(tput -T xterm sgr0)
+
+##
+## Functions
+##
+
+##
+## ┌─┐┌─┐┌─┐┌─┐┌─┐┌─┐ ┌┐┌┌─┐┌┬┐┌─┐┌─┐
+## ├┤ └─┐│ ├─┤├─┘├┤ │││├─┤│││├┤ └─┐
+## └─┘└─┘└─┘┴ ┴┴ └─┘────┘└┘┴ ┴┴ ┴└─┘└─┘
+##
+
+function escape_names {
+
+##
+## Spaces
+##
+
+name_escaped="${name// /\\ }"
+
+##
+## Commas
+##
+
+name_escaped="${name_escaped//,/\\,}"
+
+##
+## Equal Signs
+##
+
+name_escaped="${name_escaped//=/\\=}"
+
+}
+
+##
+## ┬ ┬┌─┐┌─┐┬ ┌┬┐┬ ┬ ┌─┐┬ ┬┌─┐┌─┐┬┌─
+## ├─┤├┤ ├─┤│ │ ├─┤ │ ├─┤├┤ │ ├┴┐
+## ┴ ┴└─┘┴ ┴┴─┘┴ ┴ ┴────└─┘┴ ┴└─┘└─┘┴ ┴
+##
+
+function health_check () {
+
+if [ "$healthcheck" == "true" ]; then health_check_file="health-check-${collector_type}.txt"; touch "${health_check_file}"; fi
+
+}
+
+##
+## ┌─┐┬─┐┌─┐┌─┐┌─┐┌─┐┌─┐ ┌─┐┌┬┐┌─┐┬─┐┌┬┐
+## ├─┘├┬┘│ ││ ├┤ └─┐└─┐ └─┐ │ ├─┤├┬┘ │
+## ┴ ┴└─└─┘└─┘└─┘└─┘└─┘────└─┘ ┴ ┴ ┴┴└─ ┴
+##
+
+##
+## Send Startup Event Timestamp to InfluxDB
+##
+
+function process_start () {
+
+if [ "$curl_debug" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
+
+current_time=$(date +%s)
+
+#echo "${bold}${collector_type}:${normal} time_epoch: ${current_time}"
+
+if [ -n "$influxdb_url" ]; then
+
+curl "${curl[@]}" -i -XPOST "${influxdb_url}" -u "${influxdb_username}":"${influxdb_password}" --data-binary "
+sense_o11y,host_hostname=${host_hostname},function="process_start",source=${collector_type},type=event time_epoch=${current_time}000"
+
+fi
+
+}
\ No newline at end of file
diff --git a/sense-collector/sense-collector-init.sh b/sense-collector/sense-collector-init.sh
new file mode 100644
index 0000000..e80f20b
--- /dev/null
+++ b/sense-collector/sense-collector-init.sh
@@ -0,0 +1,161 @@
+#!/bin/bash
+
+##
+## Sense Startup
+##
+
+##
+## Set Specific Variables
+##
+
+collector_type="sense-init"
+
+##
+## Sense-Collector Details
+##
+
+source sense-collector-details.sh
+
+echo "
+
+ ███████╗███████╗███╗ ██╗███████╗███████╗
+ ██╔════╝██╔════╝████╗ ██║██╔════╝██╔════╝
+ ███████╗█████╗ ██╔██╗ ██║███████╗█████╗
+ ╚════██║██╔══╝ ██║╚██╗██║╚════██║██╔══╝
+ ███████║███████╗██║ ╚████║███████║███████╗
+ ╚══════╝╚══════╝╚═╝ ╚═══╝╚══════╝╚══════╝
+
+ ██████╗ ██████╗ ██╗ ██╗ ███████╗ ██████╗████████╗ ██████╗ ██████╗
+██╔════╝██╔═══██╗██║ ██║ ██╔════╝██╔════╝╚══██╔══╝██╔═══██╗██╔══██╗
+██║ ██║ ██║██║ ██║ █████╗ ██║ ██║ ██║ ██║██████╔╝
+██║ ██║ ██║██║ ██║ ██╔══╝ ██║ ██║ ██║ ██║██╔══██╗
+╚██████╗╚██████╔╝███████╗███████╗███████╗╚██████╗ ██║ ╚██████╔╝██║ ██║
+ ╚═════╝ ╚═════╝ ╚══════╝╚══════╝╚══════╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝
+
+"
+
+debug=$SENSE_COLLECTOR_DEBUG
+debug_curl=$SENSE_COLLECTOR_DEBUG_CURL
+disable_device_details=$SENSE_COLLECTOR_DISABLE_DEVICE_DETAILS
+disable_health_check=$SENSE_COLLECTOR_DISABLE_HEALTH_CHECK
+disable_host_performance=$SENSE_COLLECTOR_DISABLE_HOST_PERFORMANCE
+disable_monitor_status=$SENSE_COLLECTOR_DISABLE_MONITOR_STATUS
+disable_sense_collector=$SENSE_COLLECTOR_DISABLE_SENSE_COLLECTOR
+host_hostname=$SENSE_COLLECTOR_HOST_HOSTNAME
+influxdb_password=$SENSE_COLLECTOR_INFLUXDB_PASSWORD
+influxdb_url=$SENSE_COLLECTOR_INFLUXDB_URL
+influxdb_username=$SENSE_COLLECTOR_INFLUXDB_USERNAME
+poll_interval=$SENSE_COLLECTOR_POLL_INTERVAL
+sense_monitor_id=$SENSE_COLLECTOR_MONITOR_ID
+sense_token=$SENSE_COLLECTOR_TOKEN
+
+if [ "$debug" == "true" ]; then
+
+echo "debug=${debug}
+debug_curl=${debug_curl}
+host_hostname=${host_hostname}
+influxdb_password=${influxdb_password}
+influxdb_url=${influxdb_url}
+influxdb_username=${influxdb_username}
+sense_monitor_id=${sense_monitor_id}
+sense_token=${sense_token}"
+fi
+
+##
+## Curl Command
+##
+
+#if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --output /dev/null --show-error --fail ); fi
+
+##
+## :::::::: :::::::: ::: ::: :::::::::: :::::::: ::::::::::: :::::::: :::::::::
+## :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+:
+## +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+
+## +#+ +#+ +:+ +#+ +#+ +#++:++# +#+ +#+ +#+ +:+ +#++:++#:
+## +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+ +#+
+## #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+# #+#
+## ######## ######## ########## ########## ########## ######## ### ######## ### ###
+##
+
+
+##
+## ┬ ┬┌─┐┌─┐┌┬┐ ┌─┐┌─┐┬─┐┌─┐┌─┐┬─┐┌┬┐┌─┐┌┐┌┌─┐┌─┐
+## ├─┤│ │└─┐ │───├─┘├┤ ├┬┘├┤ │ │├┬┘│││├─┤││││ ├┤
+## ┴ ┴└─┘└─┘ ┴ ┴ └─┘┴└─└ └─┘┴└─┴ ┴┴ ┴┘└┘└─┘└─┘
+##
+
+if [ "$disable_host_performance" != "true" ]; then
+
+while : ; do echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} Starting ${echo_bold}Host Performance${echo_normal}"; ./start-host-performance.sh ; done &
+else
+echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_DISABLE_HOST_PERFORMANCE${echo_normal} set to \"true\" or ${echo_bold}SENSE_COLLECTOR_INFLUXDB_URL${echo_normal} is missing. Disabling ${echo_color_start}host-performance${echo_normal}."
+export SENSE_COLLECTOR_DISABLE_HOST_PERFORMANCE="true"
+fi
+
+##
+## ┬ ┬┌─┐┌─┐┬ ┌┬┐┬ ┬ ┌─┐┬ ┬┌─┐┌─┐┬┌─
+## ├─┤├┤ ├─┤│ │ ├─┤───│ ├─┤├┤ │ ├┴┐
+## ┴ ┴└─┘┴ ┴┴─┘┴ ┴ ┴ └─┘┴ ┴└─┘└─┘┴ ┴
+##
+
+if [ "$disable_health_check" != "true" ]; then
+
+while : ; do echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} Starting ${echo_bold}Health Check${echo_normal}"; ./start-health-check.sh ; done &
+else
+echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_DISABLE_HEALTH_CHECK${echo_normal} set to \"true\" or ${echo_bold}SENSE_COLLECTOR_INFLUXDB_URL${echo_normal} is missing. Disabling ${echo_color_start}health-check${echo_normal}."
+export SENSE_COLLECTOR_DISABLE_HEALTH_CHECK="true"
+fi
+
+##
+## ┌┬┐┌─┐┬ ┬┬┌─┐┌─┐ ┌┬┐┌─┐┌┬┐┌─┐┬┬ ┌─┐
+## ││├┤ └┐┌┘││ ├┤───││├┤ │ ├─┤││ └─┐
+## ─┴┘└─┘ └┘ ┴└─┘└─┘ ─┴┘└─┘ ┴ ┴ ┴┴┴─┘└─┘
+##
+
+if [ "$disable_device_details" != "true" ]; then
+
+while : ; do echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} Starting ${echo_bold}Device Details${echo_normal}"; ./start-device-details.sh ; done &
+else
+echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_DISABLE_DEVICE_DETAILS${echo_normal} set to \"true\" or ${echo_bold}SENSE_COLLECTOR_INFLUXDB_URL${echo_normal} is missing. Disabling ${echo_color_start}device-details${echo_normal}."
+export SENSE_COLLECTOR_DISABLE_DEVICE_DETAILS="true"
+fi
+
+##
+## ┌┬┐┌─┐┌┐┌┬┌┬┐┌─┐┬─┐ ┌─┐┌┬┐┌─┐┌┬┐┬ ┬┌─┐
+## ││││ │││││ │ │ │├┬┘───└─┐ │ ├─┤ │ │ │└─┐
+## ┴ ┴└─┘┘└┘┴ ┴ └─┘┴└─ └─┘ ┴ ┴ ┴ ┴ └─┘└─┘
+##
+
+if [ "$disable_monitor_status" != "true" ]; then
+
+while : ; do echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} Starting ${echo_bold}Monitor Status${echo_normal}"; ./start-monitor-status.sh ; done &
+else
+echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_DISABLE_MONITOR_STATUS${echo_normal} set to \"true\" or ${echo_bold}SENSE_COLLECTOR_INFLUXDB_URL${echo_normal} is missing. Disabling ${echo_color_start}monitor-status${echo_normal}."
+export SENSE_COLLECTOR_DISABLE_MONITOR_STATUS="true"
+fi
+
+##
+## ┌─┐┌─┐┌┐┌┌─┐┌─┐ ┌─┐┌─┐┬ ┬ ┌─┐┌─┐┌┬┐┌─┐┬─┐
+## └─┐├┤ │││└─┐├┤───│ │ ││ │ ├┤ │ │ │ │├┬┘
+## └─┘└─┘┘└┘└─┘└─┘ └─┘└─┘┴─┘┴─┘└─┘└─┘ ┴ └─┘┴└─
+##
+
+if [ "$disable_sense_collector" != "true" ]; then
+
+while : ; do echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} Starting ${echo_bold}Sense Collector${echo_normal}"; ./start-sense-collector.sh ; done &
+else
+echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_DISABLE_SENSE_COLLECTOR${echo_normal} set to \"true\" or ${echo_bold}SENSE_COLLECTOR_INFLUXDB_URL${echo_normal} is missing. Disabling ${echo_color_start}sense-collector${echo_normal}."
+export SENSE_COLLECTOR_DISABLE_SENSE_COLLECTOR="true"
+fi
+
+##
+## ┌─┐┌─┐┌┐┌┌─┐┌─┐ ┬┌┐┌┬┌┬┐
+## └─┐├┤ │││└─┐├┤───│││││ │
+## └─┘└─┘┘└┘└─┘└─┘ ┴┘└┘┴ ┴
+##
+
+##
+## Used to keep dumb-init running
+##
+
+while : ; do sleep 69; done
\ No newline at end of file
diff --git a/sense-collector/start-device-details.sh b/sense-collector/start-device-details.sh
new file mode 100644
index 0000000..332fc8e
--- /dev/null
+++ b/sense-collector/start-device-details.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+
+##
+## Sense Collector - start-device-details.sh
+##
+
+##
+## Set Specific Variables
+##
+
+collector_type="device-details"
+
+##
+## Sense-Collector Details
+##
+
+source sense-collector-details.sh
+
+##
+## Set Variables from Environmental Variables
+##
+
+debug=$SENSE_COLLECTOR_DEBUG
+debug_curl=$SENSE_COLLECTOR_DEBUG_CURL
+debug_sleeping=$SENSE_COLLECTOR_DEBUG_SLEEPING
+host_hostname=$SENSE_COLLECTOR_HOST_HOSTNAME
+influxdb_password=$SENSE_COLLECTOR_INFLUXDB_PASSWORD
+influxdb_url=$SENSE_COLLECTOR_INFLUXDB_URL
+influxdb_username=$SENSE_COLLECTOR_INFLUXDB_USERNAME
+poll_interval=$SENSE_COLLECTOR_DEVICE_DETAILS_POLL_INTERVAL
+sense_monitor_id=$SENSE_COLLECTOR_MONITOR_ID
+sense_token=$SENSE_COLLECTOR_TOKEN
+threads=$SENSE_COLLECTOR_THREADS
+
+##
+## Check for required intervals
+##
+
+if [ -z "${poll_interval}" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_DEVICE_DETAILS_POLL_INTERVAL${echo_normal} environmental variable not set. Defaulting to ${echo_bold}60${echo_normal} seconds."; poll_interval="60"; export SENSE_COLLECTOR_POLL_INTERVAL="60"; fi
+
+if [ -z "${host_hostname}" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_HOST_HOSTNAME${echo_normal} environmental variable not set. Defaulting to ${echo_bold}sense-collector${echo_normal}."; host_hostname="sense-collector"; export SENSE_COLLECTOR_HOST_HOSTNAME="sense-collector"; fi
+
+if [ -z "${threads}" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_THREADS${echo_normal} environmental variable not set. Defaulting to ${echo_bold}4${echo_normal} threads."; threads="4"; export SENSE_COLLECTOR_THREADS="4"; fi
+
+if [ "$debug" == "true" ]
+
+then
+
+echo "$(date) - Starting Sense Collector (start-device-details.sh) - https://github.com/lux4rd0/sense-collector
+
+Debug Environmental Variables
+
+debug=${debug}
+debug_curl=${debug_curl}
+host_hostname=${host_hostname}
+influxdb_password=${influxdb_password}
+influxdb_url=${influxdb_url}
+influxdb_username=${influxdb_username}
+poll_interval=${poll_interval}
+sense_monitor_id=${sense_monitor_id}
+sense_token=${sense_token}"
+fi
+
+##
+## Send Startup Event Timestamp to InfluxDB
+##
+
+process_start
+
+##
+## Curl Command
+##
+
+if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --show-error --fail ); fi
+
+##
+## Start Sense Device Details Loop
+##
+
+while ( true ); do
+before=$(date +%s%N)
+
+./exec-device-details.sh
+
+after=$(date +%s%N)
+delay=$(echo "scale=4;(${poll_interval}-($after-$before) / 1000000000)" | bc)
+
+if [ "$debug_sleeping" == "true" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} Sleeping: ${delay} seconds"; fi
+
+sleep "$delay"
+done
\ No newline at end of file
diff --git a/sense-collector/start-host-performance.sh b/sense-collector/start-host-performance.sh
new file mode 100644
index 0000000..8bc263a
--- /dev/null
+++ b/sense-collector/start-host-performance.sh
@@ -0,0 +1,88 @@
+#!/bin/bash
+
+##
+## Sense Collector - start-host-performance.sh
+##
+
+##
+## Set Specific Variables
+##
+
+collector_type="host-performance"
+
+##
+## Sense-Collector Details
+##
+
+source sense-collector-details.sh
+
+##
+## Set Variables from Environmental Variables
+##
+
+debug=$SENSE_COLLECTOR_DEBUG
+debug_curl=$SENSE_COLLECTOR_DEBUG_CURL
+debug_sleeping=$SENSE_COLLECTOR_DEBUG_SLEEPING
+function=$SENSE_COLLECTOR_FUNCTION
+healthcheck=$SENSE_COLLECTOR_HEALTHCHECK
+host_hostname=$SENSE_COLLECTOR_HOST_HOSTNAME
+perf_interval=$SENSE_COLLECTOR_PERF_INTERVAL
+influxdb_password=$SENSE_COLLECTOR_INFLUXDB_PASSWORD
+influxdb_url=$SENSE_COLLECTOR_INFLUXDB_URL
+influxdb_username=$SENSE_COLLECTOR_INFLUXDB_USERNAME
+poll_interval=$SENSE_COLLECTOR_HOST_PERFORMANCE_POLL_INTERVAL
+
+if [ "$debug" == "true" ]
+
+then
+
+echo "${echo_bold}${echo_color_host_performance}${collector_type}:${echo_normal} $(date) - Starting Sense Collector (start-host-performance.sh) - https://github.com/lux4rd0/sense-collector
+
+Debug Environmental Variables
+
+collector_type=${collector_type}
+debug=${debug}
+debug_curl=${debug_curl}
+debug_sleeping=${debug_sleeping}
+function=${function}
+healthcheck=${healthcheck}
+host_hostname=${host_hostname}
+perf_interval=${perf_interval}
+weatherflow_collector_version=${weatherflow_collector_version}"
+
+fi
+
+##
+## Check for required intervals
+##
+
+if [ -z "${poll_interval}" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_POLL_INTERVAL${echo_normal} environmental variable not set. Defaulting to ${echo_bold}60${echo_normal} seconds."; poll_interval="60"; export SENSE_COLLECTOR_POLL_INTERVAL="60"; fi
+
+if [ -z "${host_hostname}" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_HOST_HOSTNAME${echo_normal} environmental variable not set. Defaulting to ${echo_bold}sense-collector${echo_normal}."; host_hostname="sense-collector"; export SENSE_COLLECTOR_HOST_HOSTNAME="sense-collector"; fi
+
+##
+## Send Startup Event Timestamp to InfluxDB
+##
+
+process_start
+
+##
+## Curl Command
+##
+
+if [ "$debug" == "true" ]; then curl=( ); else curl=( --silent --show-error --fail ); fi
+
+##
+## Start Host Performance Loop
+##
+
+while ( true ); do
+before=$(date +%s%N)
+
+./exec-host-performance.sh
+
+after=$(date +%s%N)
+delay=$(echo "scale=4;(${perf_interval}-($after-$before) / 1000000000)" | bc)
+if [ "$debug_sleeping" == "true" ]; then echo "${echo_bold}${echo_color_host_performance}${collector_type}:${echo_normal} Sleeping: ${delay} seconds"; fi
+sleep "$delay"
+done
\ No newline at end of file
diff --git a/sense-collector/start-monitor-status.sh b/sense-collector/start-monitor-status.sh
new file mode 100644
index 0000000..bcd7b00
--- /dev/null
+++ b/sense-collector/start-monitor-status.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+
+##
+## Sense Collector - start-monitor-status.sh
+##
+
+##
+## Set Specific Variables
+##
+
+collector_type="monitor-status"
+
+##
+## Sense-Collector Details
+##
+
+source sense-collector-details.sh
+
+##
+## Set Variables from Environmental Variables
+##
+
+debug=$SENSE_COLLECTOR_DEBUG
+debug_curl=$SENSE_COLLECTOR_DEBUG_CURL
+debug_sleeping=$SENSE_COLLECTOR_DEBUG_SLEEPING
+host_hostname=$SENSE_COLLECTOR_HOST_HOSTNAME
+influxdb_password=$SENSE_COLLECTOR_INFLUXDB_PASSWORD
+influxdb_url=$SENSE_COLLECTOR_INFLUXDB_URL
+influxdb_username=$SENSE_COLLECTOR_INFLUXDB_USERNAME
+poll_interval=$SENSE_COLLECTOR_MONITOR_STATUS_POLL_INTERVAL
+sense_monitor_id=$SENSE_COLLECTOR_MONITOR_ID
+sense_token=$SENSE_COLLECTOR_TOKEN
+threads=$SENSE_COLLECTOR_THREADS
+
+##
+## Check for required intervals
+##
+
+if [ -z "${poll_interval}" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_POLL_INTERVAL${echo_normal} environmental variable not set. Defaulting to ${echo_bold}60${echo_normal} seconds."; poll_interval="60"; export SENSE_COLLECTOR_POLL_INTERVAL="60"; fi
+
+if [ -z "${host_hostname}" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_HOST_HOSTNAME${echo_normal} environmental variable not set. Defaulting to ${echo_bold}sense-collector${echo_normal}."; host_hostname="sense-collector"; export SENSE_COLLECTOR_HOST_HOSTNAME="sense-collector"; fi
+
+if [ -z "${threads}" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_THREADS${echo_normal} environmental variable not set. Defaulting to ${echo_bold}4${echo_normal} threads."; threads="4"; export SENSE_COLLECTOR_THREADS="4"; fi
+
+if [ "$debug" == "true" ]
+
+then
+
+echo "$(date) - Starting Sense Collector (start-monitor-status.sh) - https://github.com/lux4rd0/sense-collector
+
+Debug Environmental Variables
+
+debug=${debug}
+debug_curl=${debug_curl}
+host_hostname=${host_hostname}
+influxdb_password=${influxdb_password}
+influxdb_url=${influxdb_url}
+influxdb_username=${influxdb_username}
+poll_interval=${poll_interval}
+sense_monitor_id=${sense_monitor_id}
+sense_token=${sense_token}"
+fi
+
+##
+## Send Startup Event Timestamp to InfluxDB
+##
+
+process_start
+
+##
+## Curl Command
+##
+
+if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --show-error --fail ); fi
+
+##
+## Start Sense Monitor Status Loop
+##
+
+while ( true ); do
+before=$(date +%s%N)
+
+./exec-monitor-status.sh
+
+after=$(date +%s%N)
+delay=$(echo "scale=4;(${poll_interval}-($after-$before) / 1000000000)" | bc)
+
+if [ "$debug_sleeping" == "true" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} Sleeping: ${delay} seconds"; fi
+
+sleep "$delay"
+done
\ No newline at end of file
diff --git a/sense-collector/start-sense-collector.sh b/sense-collector/start-sense-collector.sh
new file mode 100644
index 0000000..2572bf4
--- /dev/null
+++ b/sense-collector/start-sense-collector.sh
@@ -0,0 +1,81 @@
+#!/bin/bash
+
+##
+## Sense Collector - start-sense-collector.sh
+##
+
+##
+## Set Specific Variables
+##
+
+collector_type="sense-collector"
+
+##
+## Sense-Collector Details
+##
+
+source sense-collector-details.sh
+
+##
+## Set Variables from Environmental Variables
+##
+
+debug=$SENSE_COLLECTOR_DEBUG
+debug_curl=$SENSE_COLLECTOR_DEBUG_CURL
+debug_sleeping=$SENSE_COLLECTOR_DEBUG_SLEEPING
+host_hostname=$SENSE_COLLECTOR_HOST_HOSTNAME
+influxdb_password=$SENSE_COLLECTOR_INFLUXDB_PASSWORD
+influxdb_url=$SENSE_COLLECTOR_INFLUXDB_URL
+influxdb_username=$SENSE_COLLECTOR_INFLUXDB_USERNAME
+poll_interval=$SENSE_COLLECTOR_SENSE_COLLECTOR_POLL_INTERVAL
+sense_monitor_id=$SENSE_COLLECTOR_MONITOR_ID
+sense_token=$SENSE_COLLECTOR_TOKEN
+threads=$SENSE_COLLECTOR_THREADS
+
+##
+## Check for required intervals
+##
+
+if [ -z "${poll_interval}" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_SENSE_COLLECTOR_POLL_INTERVAL${echo_normal} environmental variable not set. Defaulting to ${echo_bold}60${echo_normal} seconds."; poll_interval="60"; export SENSE_COLLECTOR_SENSE_COLLECTOR_POLL_INTERVAL="60"; fi
+
+if [ -z "${host_hostname}" ]; then echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} ${echo_bold}SENSE_COLLECTOR_HOST_HOSTNAME${echo_normal} environmental variable not set. Defaulting to ${echo_bold}sense-collector${echo_normal}."; host_hostname="sense-collector"; export SENSE_COLLECTOR_HOST_HOSTNAME="sense-collector"; fi
+
+if [ "$debug" == "true" ]
+
+then
+
+echo "$(date) - Starting Sense Collector (start-sense-collector.sh) - https://github.com/lux4rd0/sense-collector
+
+Debug Environmental Variables
+
+collector_type=${collector_type}
+debug=${debug}
+debug_curl=${debug_curl}
+host_hostname=${host_hostname}
+influxdb_password=${influxdb_password}
+influxdb_url=${influxdb_url}
+influxdb_username=${influxdb_username}
+poll_interval=${poll_interval}
+sense_monitor_id=${sense_monitor_id}
+sense_token=${sense_token}
+threads=${threads}"
+fi
+
+##
+## Send Startup Event Timestamp to InfluxDB
+##
+
+process_start
+
+##
+## Curl Command
+##
+
+if [ "$debug_curl" == "true" ]; then curl=( ); else curl=( --silent --show-error --fail ); fi
+
+##
+## Start Sensor Collector Socket Connection
+##
+
+echo "${echo_bold}${echo_color_random}${collector_type}:${echo_normal} Starting up ${echo_bold}${echo_color_start}sense_collector${echo_normal} - Stream Type: ${echo_bold}${poll_interval}${echo_normal}."
+timeout 10m ./websocat_amd64-linux-static -n -t - autoreconnect:wss://clientrt.sense.com/monitors/"${sense_monitor_id}"/realtimefeed -H "Authorization: bearer ${sense_token}" -H "Sense-Collector-Client-Version: 1.0.0" -H "X-Sense-Protocol: 3" -H "User-Agent: okhttp/3.8.0" | ./exec-sense-collector.sh
\ No newline at end of file
diff --git a/sense-collector/start.sh b/sense-collector/start.sh
deleted file mode 100644
index 56e40cf..0000000
--- a/sense-collector/start.sh
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/bin/bash
-
-##
-## Sense Startup
-##
-
-echo "
-
- ███████╗███████╗███╗ ██╗███████╗███████╗
- ██╔════╝██╔════╝████╗ ██║██╔════╝██╔════╝
- ███████╗█████╗ ██╔██╗ ██║███████╗█████╗
- ╚════██║██╔══╝ ██║╚██╗██║╚════██║██╔══╝
- ███████║███████╗██║ ╚████║███████║███████╗
- ╚══════╝╚══════╝╚═╝ ╚═══╝╚══════╝╚══════╝
-
- ██████╗ ██████╗ ██╗ ██╗ ███████╗ ██████╗████████╗ ██████╗ ██████╗
-██╔════╝██╔═══██╗██║ ██║ ██╔════╝██╔════╝╚══██╔══╝██╔═══██╗██╔══██╗
-██║ ██║ ██║██║ ██║ █████╗ ██║ ██║ ██║ ██║██████╔╝
-██║ ██║ ██║██║ ██║ ██╔══╝ ██║ ██║ ██║ ██║██╔══██╗
-╚██████╗╚██████╔╝███████╗███████╗███████╗╚██████╗ ██║ ╚██████╔╝██║ ██║
- ╚═════╝ ╚═════╝ ╚══════╝╚══════╝╚══════╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝
-
-"
-
-debug=$SENSE_COLLECTOR_DEBUG
-debug_curl=$SENSE_COLLECTOR_DEBUG_CURL
-influxdb_password=$SENSE_COLLECTOR_INFLUXDB_PASSWORD
-influxdb_url=$SENSE_COLLECTOR_INFLUXDB_URL
-influxdb_username=$SENSE_COLLECTOR_INFLUXDB_USERNAME
-sense_monitor_id=$SENSE_COLLECTOR_MONITOR_ID
-sense_token=$SENSE_COLLECTOR_TOKEN
-
-if [ "$debug" == "true" ]; then
-
-echo "debug=${debug}
-debug_curl=${debug_curl}
-influxdb_password=${influxdb_password}
-influxdb_url=${influxdb_url}
-influxdb_username=${influxdb_username}
-sense_monitor_id=${sense_monitor_id}
-sense_token=${sense_token}"
-fi
-
-if [ "$disable_host_performance" != "true" ]; then
-
-while : ; do echo "$(date) - Starting up Sense Collector."; timeout 10m ./websocat_amd64-linux-static -n -t - autoreconnect:wss://clientrt.sense.com/monitors/${sense_monitor_id}/realtimefeed -H "Authorization: bearer ${sense_token}" -H "Sense-Collector-Client-Version: 1.0.0" -H "X-Sense-Protocol: 3" -H "User-Agent: okhttp/3.8.0" | ./exec-sense-collector.sh ; done
-else
-echo "${echo_bold}${echo_color_start}${collector_type}:${echo_normal} $(date) - ${echo_bold}SENSE_COLLECTOR_DISABLE_HOST_PERFORMANCE${echo_normal} set to \"true\" or ${echo_bold}SENSE_COLLECTOR_INFLUXDB_URL${echo_normal} is missing. Disabling ${echo_color_start}host-performance${echo_normal}."
-export SENSE_COLLECTOR_DISABLE_HEALTHCHECK_HOST_PERFORMANCE="true"
-fi
\ No newline at end of file