Saturday, 26 April 2025

Pi 5 Birdbox Webcam Setup

Installing a H264 webcam on a Raspberry Pi.

The following can be completed in 15-30 minutes...

Ingredients

  • Memory Card for the Pi (e.g. 16Gb)
  • Raspberry Pi 5
  • USB Webcam


Installing The Operating System

On a Windows or Linux PC, Run the Raspberry Pi Imager, and Select:

  • Raspberry Pi 5
  • Select Rasberry Pi OS (64-bit)
  • Your memory card (e.g. 16Gb)

Edit Settings, and:

  • Set the device name, e.g. 'camserver'
  • Fill in your Username, Password, WiFi SSID and Password
  • Under 'Services', ensure that SSH is enabled, and select 'Use password authentication'

Then select Install, and wait.

Booting the Pi

Connect a webcam (preferably one supporting H.264)
Insert the memory card into the Pi, and power up
The LED will flash Red, then Green, and after that will blink green every time the memory card is accessed.
If the LED remains Red, this indicates that your power supply cannot provide sufficient current.

Logging In
ssh -l user camserver.local

Disabling Swapping to Memory Card
sudo systemctl disable dphys-swapfile.service
  sync; sudo reboot

Install Docker
# Remove conflicting packages (from a fresh install, there should be none)
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done

# Add Docker's official GPG key:

sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/raspbian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:

echo "Add the repository to Apt sources:"
echo \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo apt-get -y update
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker
sudo apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Test Docker

sudo docker run hello-world

Install Restreamer
sudo docker run -d --restart=always --name restreamer \
    -v /opt/restreamer/config:/core/config \
    -v /opt/restreamer/data:/core/data \
    --privileged \
    -p 8080:8080 \
    -p 8181:8181 \
    -p 1935:1935 \
    -p 1936:1936 \
    -p 6000:6000 \
    datarhei/restreamer:latest

Locate the Webcam

lsusb

Bus 001 Device 013: ID 05a3:9331 ARC International Camera


v4l2-ctl --list-devices

HD Web Camera: HD Web Camera (usb-xhci-hcd.0-1):
/dev/video0
/dev/video1
/dev/media3

Configure Restreamer

Connect to: http://camserver.local:8080/ui/
  * Set admin username / password
  * Select video device /dev/video0
  * Select format: H.264, Framerate: 25, Size: 1920x1080, Probe
  * Select the H264 Stream
  * Select Codec: Passthrough / Copy
  * Select Audio source from webcam
  * Select 44100 rate, 1 channel and Probe
  * Select stream






Monday, 31 March 2025

ESP32 C3 Mini - Ubuntu, PlatformIO - Error: esp_usb_jtag: could not find or open device!

When debugging the ESP32 C3 device using Ubuntu and PlatformIO, the error message as follows is seen:

...
Info : esp_usb_jtag: VID set to 0x303a and PID to 0x1001
Info : esp_usb_jtag: capabilities descriptor set to 0x2000
Warn : Transport "jtag" was already selected
Error: libusb_open() failed with LIBUSB_ERROR_ACCESS
Error: esp_usb_jtag: could not find or open device!
...

Steps to debug:

1. Ensure user is in the plugdev group

groups
sudo vi /etc/group

If you have added the user to the group, remember to log out and back in 

2. Try adding a udev rule:

lsusb

Bus 003 Device 071: ID 303a:1001 Espressif USB JTAG/serial debug unit

  

sudo vi /etc/udev/rules.d/99-arduino.rules

# ESP32 C3

SUBSYSTEM=="tty", ENV{ID_REVISION}=="303a", ENV{ID_MODEL_ID}=="1000", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_CANDIDATE}="0"

SUBSYSTEM=="usb", ATTR{idVendor}=="303a", ATTR{idProduct}=="1001", MODE="0666", GROUP="plugdev", ENV{ID_MM_DEVICE_IGNORE}="1"

sudo udevadm control --reload-rules



Wednesday, 5 February 2025

Home Assistant: Response to Google Home Query With Web Data

This example creates an automation, which uses configuration data from a currently playing media stream to acquire a what's playing response, then broadcasts that response back to the user via one or more google smart speakers.

Note that it is not currently (February 2025) possible to work out which speaker raised the actual request when processing commands with Home Assistant; and it is also not possible to respond on a speaker without announcing the response as a "broadcast message".

This method does, however, allow the response to temporarily reduce the volume of playing media and restore it after the message has been played.

There are 4 Steps required:

1. Create a rest_command by editing configuration.yaml

2. Create a Home Assistant automation, which will perform the actual sequence, calling the rest_command when json information needs to be retrieved from a web resource.

3. Create a Home Assistant Script, which will be used by Google Home to trigger the automation.

4. Create a Google Home Automation to trigger the script.


Step 1: Create a rest_command

Edit the configuration.yaml file, and add an include line for a rest_command.yaml file.

$ sudo vi configuration.yaml

...

rest_command: !include rest_command.yaml

Note that there is no indentation
Now create a rest_command.yaml file, and add the contents as follows:

$ vi rest_command.yaml

  fetch:
    url: "{{ url }}"
    method: GET

Note that the indentation is important, i.e.:

fetch:
url: "{{ url }}"
method: GET

The above example is a generic one (works for any website).  It may make things more readable / make things more maintainable if multiple entries are used for different information sources, for example:
  recipe_json:
    url: "https://dummyjson.com/recipes/{{ index }}"
    method: GET
In the above example, the 'index' variable will be used to retrieve the requested recipe.

Once the configuration.yaml and rest_command.yaml files have been created, select "Developer Tools / YAML / Check Configuration", and then "Restart" (if everything checks out OK).


Step 2: Create a Home Assistant automation

Create a new automation with Settings / Automations / Create Automation, and edit as YAML, and paste in the code below:

alias: "Automation: Recipe Information"
description: ""
triggers: []
conditions: []
actions:
  - action: rest_command.fetch
    data:
      url: https://dummyjson.com/recipes/1
    metadata: {}
    response_variable: json_response
  - action: notify.google_assistant_sdk
    metadata: {}
    data:
      message: Recipe for {{ json_response['name'] }} has {{ json_response['caloriesPerServing'] }} calories per serving
      target: my speaker
mode: single

The code accesses the dummyjson.com/recipes/1 website, and downloads the json output.  The actual output data is stored in the json_response variable as part of the first action.

The second action generates a notification message (broadcast) to the speaker called "My Speaker" extracting information from the json response to build up the string.

Select the automation and then 'Settings' from the ... menu, and ensure that a meaningful Entity ID is chosen - in this case, automation.recipe_information has been selected.  This is the handle by which the automation can be programmatically be launched.


You can perform a test run of this script by selecting "Run Actions" which is found in the ... menu for the automation entry, and when run, you should hear the speaker saying "Broadcast Message: Recipe for Classic Margherita Pizza has 300 calories per serving".

Presently (February 2025), there is no known way to send the message without the "Broadcast Message" prefix. There is also no known way of working out which speaker the request actually came from.  Finally, the ability to speak requests with parameters was removed by Google a couple of years ago.

Step 3: Create a Home Assistant Script

Now the Google Home application can't launch a Home Assistant automation, but it can launch a script, (it thinks the script is a scene) and the script in turn can kick off the automation.

Select Settings / Automation and Scenes / Scripts / Create Script, and edit as YAML, and paste in the code below:
alias: "HaScript: Recipe Information"
description: ""
sequence:
  - action: automation.trigger
    metadata: {}
    data:
      skip_condition: false
    target:
      entity_id: automation.recipe_information

Again, you can select the ... against the script and run it, and the speaker should tell you about the recipe again.


Step 4: Create a Google Home Automation

Finally, if you say "Sync My Devices" on your smart speaker, you should then be able to go into the Google Home app, and create a new Automation:
When I say to my Assistant: "Recipe Information"
Select Scene: HaScript: Recipe Information

Now when you say "Recipe Information",  the speaker called "My Speaker" will say "Broadcast Message: Recipe for Classic Margherita Pizza has 300 calories per serving"

What's Next

This is a relatively trivial example, but scripts can be performed using resources from any website, along with any entity resource already available within home assistant.  An example idea:
Triggered automation fires at 6pm:
  If the user is at home - checking {{states('sensor.myphone_geocoded_location')}}
    Fetch the horoscope of the day - using rest_command.fetch_horoscope
    And broadcast the horoscope on the bedroom speaker - using notify.googleassistant_sdk
    And also send the horoscope to the user - using notify.mobile_app_myphone






Sunday, 2 February 2025

Home Assistant: Multi-Entity Sensor

This post is part of a group of posts all associated with the acquisition of information from external resources and making it available in Home Assistant.

Simple Sensor

In order to set up Home Assistant to collect data at regular intervals from web sites / devices and make the information available, sensors can be used.

This sensor uses a rest mechanism to extract multiple parameters from a single request / call.

Step 1: Include File for Sensor Settings

Update 'configuration.yaml' to include a separate file which will include all of the sensor configurations:

$ sudo vi configuration.yaml

...

rest: !include rest.yaml

 Step 2: Add a Sensor

Create a 'rest.yaml' file alongside the 'configuration.yaml' file

$ vi rest.yaml

- method: GET

  scan_interval: 900
  resource: "https://api.openweathermap.org/data/2.5/weather?lat=51.1789&lon=-1.8262&appid=MySecret"

  sensor: 

  - name: "Test Weather - Deprecated"
    value_template: "{{ value_json['weather'][0]['description'] | default('unknown') }}"
    json_attributes_path: "$.['main']"
    json_attributes:
      - temp
      - feels_like
      - pressure
      - humidity 

  - name: "Test Weather: Description"
    value_template: "{{ value_json['weather'][0]['description'] | default('unknown') }}" 


  - name: "Test Weather: Feels Like"
    value_template: "{{ value_json['main']['feels_like'] - 273.15 }}"
    unit_of_measurement: "°C" 


  - name: "Test Weather: Temperature"
    value_template: "{{ value_json['main']['temp'] - 273.15 }}"
    unit_of_measurement: "°C"

Note: In order to get your own "MySecret", head on over to openweathermap.org and set up a free account.

Hint: The indentation is critical, so in case the indentation above is unclear, see the clarification below:

- method: ...

␠␠scan_interval: ...

␠␠resource: ...

␠␠sensor: 

␠␠␠␠- name: ...

␠␠␠␠value_template: ...

␠␠␠␠json_attributes:

␠␠␠␠␠␠- attribute_1

Step 3: Check Things

Visit your Home Assistant / Developer Tools / Yaml page, and select "check configuration", and if everything is OK, select "Restart".

Once restarted, visit Home Assistant / Settings / Developer Tools / States, and search for "Test Weather" in the list and you should see something similar to the following.


What have we Just Done

We have changed the configuration of Home Assistant to use a separate rest.yaml file - this allows us to add sensors with multiple variables / values.

Every 15 minutes (900 seconds) in this example, a JSON output will be requested from the given resource, and the contents parsed.

We have added variables / values from a single web call in the rest.yaml using two different methods.

The first entry uses a mechanism which is considered deprecated.  This returns the 'description' as the state using the path: {{ value_json['weather'][0]['description'] | default('unknown') }}, and then adds the tempfeels_like, etc. as attributes. 

The next three entries return the same information, but this time, each of the fields is returned as the state. Here, it is possible to process the data before returning it as the state (in this case, the temperature is converted from degrees Kelvin to degrees centigrade), and also possible to return a default value in the event that the connection is not working.

In order to find the correct paths to use with the value_json syntax, it is recommended that you paste your 'resource' url into a web browser, and copy the resulting json into the form at the JSON Path Finder, this will parse the JSON, and allow you to click on a field within the data and extract the corresponding path.



In the above example, the JSON has come from the openweathermap website, and the path for the description is x.weather[0].description.  

This would be translated to value_json["weather"][0]['description'] in the Home Assistant settings.







Saturday, 1 February 2025

Home Assistant: Simple Sensor

This post is part of a group of posts all associated with the acquisition of information from external resources and making it available in Home Assistant.

Simple Sensor

In order to set up Home Assistant to collect data at regular intervals from web sites / devices and make the information available, sensors can be used.

This is a really simple example.

Step 1: Include File for Sensor Settings

Update 'configuration.yaml' to include a separate file which will include all of the sensor configurations:

$ sudo vi configuration.yaml

...

sensor: !include sensors.yaml

Note: This is 'sensor:' and not 'sensors:'

 Step 2: Add a Sensor

Create a 'sensors.yaml' file alongside the 'configuration.yaml' file

$ vi sensors.yaml

- platform: rest
 name: "Json Test - Random Number"
 resource: "http://www.randomnumberapi.com/api/v1.0/randomnumber"
 method: GET
 value_template: "{{ value_json[0] }}"
 scan_interval: 900
Note: The lines are not indented at all

Step 3: Check Things

Visit your Home Assistant / Developer Tools / Yaml page, and select "check configuration", and if everything is OK, select "Restart".

Once restarted, visit Home Assistant / Settings / Device and Services / Entities, and search for "Json Test - Random Number" in the list - select it, and you should see the results (in this case, the random number is 48).


What have we Just Done

We have changed the configuration of Home Assistant to use a separate sensors.yaml file - this allows us to keep all of our sensors together.

We have added a sensor to the sensors.yaml file.

Every 15 minutes (900 seconds) in this example, a JSON file will be extracted from the given resource, and the contents parsed.

The first entry will be pulled out of the json file, and will be used for the "Json Test - Random Number".

Note that this is a really simple example, and the returned JSON is just a list of just one number.

It is likely that you will have a more complex result, and you will need to replace the 'value_json[0]' with a more complex search pattern.

In this case, it is recommended that you paste your 'resource' url into a web browser, and copy the resulting json into the form at the JSON Path Finder, this will parse the JSON, and allow you to click on a field within the data and extract the corresponding path.


In the above example, the JSON has come from the openweathermap website, and the path for the description is x.weather[0].description.  

This would be translated to value_json["weather"][0]['description'] in the Home Assistant settings.