Thursday, 27 August 2026

Connecting Silvercrest SBF75 Scales to An Android Phone using OpenScales and Google Fit

Overview

This guide includes the steps to connect a Silvercrest SBF75 set of scales to Google Fit on an Android Phone using the OpenScales application.
The default application for the scales is the Health4You app, and this application is not compatible with the "Health Connect" system for data sharing.  The basic steps and applications are:
  • Health4You app to configure the scales
  • openScales sync to connect the phone to the scales
  • openScales to manage the scales data
  • Health Connect as a central hub for health data management / distribution
  • Google Fit as the end application for the data

Setting Up Scales - e.g. Silvercrest SBF75

1. Ensure scales are unpaired in Android bluetooth settings

2. Start Health4You app, and delete any old scale pairings

3. Ensure your sex, height and date of birth are correctly set in Personal Info

4. Turn on scales and hold button on back until 'DEL' appears

5. Launch Health4You and add Device - select Scales

6. Turn on scales as prompted, and select Connect. Connection should be quick

7. Assign Initials and ± weight configuration

8. Take reference measurement as prompted

Gotcha: If scales are not detected quickly and you have a newer phone, try enabling "Show unsupported BLE devices" in "Settings / Developer options / More settings / Networking" on the Android device.  To enable the Developer menu in settings, go to "Settings / About phone / Software information" and tap Build number 7 times consecutively to enable developer mode - you will then need to enter your pin/password to enable it.  After that, the Developer options menu will be available.  You can just type "unsupported" in the settings search to jump the relevant entry.


OpenScales

9. Start OpenScales and delete any saved scales

10. Ensure your sex, height and date of birth are correctly set in 'Users' (add a user if not already there)

11. Turn in scales and select Settings / Bluetooth, and search for the scales

      e.g. Silvercrest SBF75 3C:A3:08:20:33:8C -48dbm

12. Select the scales, or click on the ... and select "Save as preferred"

13. Turn on the scales, and select Settings / Connections / Bluetooth, and pair the scales (use a PIN of '0000')

14. Stand on the scales, and take a measurement - you should see your initials displayed

15. Step off the scales, and whilst the screen is still on, select the bluetooth icon in OpenScales

    This should transfer the measurements made into OpenScales

Gotcha: if you don't have the same initials and date of birth in the scales (set through Health4You) and OpenScales, the system may complain that there are too many users.


OpenScale Sync

16. Install openScale Sync, and launch it

17. Ensure that "openScale" is identified as the data source

18. Ensure that HealthConnect is enabled, and select your openScale user

19. Select sync direction as 'export' or 'both'

20. Select 'Test Connection'

21. Select 'Sync with Health Connect'

Gotcha: "HealthConnectException: API call quota exceeded. Available quota: 0.1, requested: 1" - Wait 5 minutes and try again!


Google Fit and Health Connect

22. Launch Google Fit, and select your profile (figure in bottom right of screen)

23. Select the setup gears at the top of the screen

24. Ensure "Sync Fit with Health Connect" is enabled

25. Select Manage Data and Access

26. In "Fit", ensure that Body Measurements are "Allowed to Read", and Body Measurements are "NOT Allowed to Write"

27. In "openScale sync", ensure that Body Measurements are "Allowed to Read" and also "Allowed to Write"


Tuesday, 27 January 2026

Birdbox Camera Server on a Pi5 Using Motion and Icecast2

This guide shows how to create a Pi5 Birdbox Camera Server, which provides separate streaming servers for both audio and video.

It does not combine the two streams together - for that, motionplus or restreamer should be investigated.



Finding the Webcam Device

To ensure that the camera can always be found, even if devices are set-up in a different order, or the device is plugged into a different USB port, we use udev, so the webcam video will always be at /dev/webcam_video, instead of /dev/video0, /dev/video1 etc.

Similarly, the audio applications can find the device using hw:CARD=webcam_audio,DEV=0 instead of hw:0,0, hw:2,0 etc.

1. Find the USB webcam

# lsusb

Bus 003 Device 002: ID 1bcf:2cc9 Sunplus Innovation Technology Inc. USB 2.0 Camera

2. Create a webcam rule file for udev, using the USB vendor and product IDs from the lsusb.

# sudo vi /etc/udev/rules.d/99-webcam.rules

# Webcam USB Device: Audio

SUBSYSTEM=="sound", ATTRS{idVendor}=="1bcf", ATTRS{idProduct}=="2cc9", ATTR{id}="webcam_audio",  SYMLINK+="webcam_audio"

# Webcam USB Device: Video

SUBSYSTEM=="video4linux", ATTRS{idVendor}=="1bcf", ATTRS{idProduct}=="2cc9", ATTR{id}="webcam_video", SYMLINK+="webcam_video"

3. Now refresh the udev subsystem

# sudo udevadm control --reload-rules
# sudo udevadm trigger

4. Finally, unplug and re-plug the USB device (or reboot)

5. Check the device is correctly installed

# ls -la /dev/webcam*

lrwxrwxrwx 1 root root 13 Jan 27 12:43 /dev/webcam_audio -> snd/controlC2
lrwxrwxrwx 1 root root  6 Jan 27 12:43 /dev/webcam_video -> video0

# arecord -l

**** List of CAPTURE Hardware Devices ****
card 2: webcam_audio [USB 2.0 Camera], device 0: USB Audio [USB Audio]
  Subdevices: 0/1
  Subdevice #0: subdevice #0

Note that the two softlinks created in /dev point to the webcam video stream device and the audio control device, and the name reported for the capture card for audio is webcam_audio.

Tthe webcam video device can be referred to as: /dev/webcam_video

And the audio hardware card/port can be referred to as: hw:CARD=webcam_audio,DEV=0


Installing and Configuring the Video Server

1. Install the motion server

# sudo apt-get install motion 

2. Use a USB Device

It may be sensible to use a USB device for capturing video and photo sequences (if they are to be enabled).  If so, plug the device in, and check its name in /media/username.  Make sure that there is a folder on the USB device that can be written to by the 'motion' user.

# sudo mkdir /media/username/device/motion
# sudo chown motion:motion /media/username/device/motion
# sudo chmod 775 /media/username/device/motion

3. Configure the server, by editing the motion.conf in the /etc area, and ensuring the following adjustments are made:

# sudo vi /etc/motion/motion.conf

# Run in foreground (because we are going to manage this with systemd)
daemon off

# Adjust this if you wish (perhaps to point to a USB mounted device)
target_dir /var/lib/motion

# Set the video device to the udev-managed /dev/webcam_video (not /dev/video0 etc.)
video_device /dev/webcam_video

# Adjust the image capture size to one of the sizes supported by the webcam
width 1920
height 1080

# Adjust the framerate to something reasonable for the camera and USB interface
framerate 20

# Text to be overlayed in the lower left corner of images
text_left BIRDBOX

# Output pictures when motion is detected
picture_output off

# Create movies of motion events.
movie_output on
4. Start the video server
# sudo systemctl start motion
# sudo systemctl enable motion
# sudo systemctl status motion

motion.service - Motion detection video capture daemon
     Loaded: loaded (/usr/lib/systemd/system/motion.service; enabled; preset: enabled)
     Active: active (running) since Mon 2026-01-26 20:39:13 GMT; 16h ago
 Invocation: c3ff5f5b74554ee9afc7e079f63b98ef
       Docs: man:motion(1)
   Main PID: 647 (motion)
      Tasks: 4 (limit: 4762)
        CPU: 18min 3.364s
     CGroup: /system.slice/motion.service
             └─647 /usr/bin/motion

Installing and Configuring the Media Server

1. Install the media server

# sudo apt-get install icecast2

2. Setup the username / password etc.

Edit the configuration file, and set a source, relay and admin username and password:

# sudo vi /etc/icecast2/icecast.xml

    <authentication>
        <!-- Sources log in with username 'source' -->
        <source-password>source</source-password>
        <!-- Relays log in with username 'relay' -->
        <relay-password>relay</relay-password>
        <!-- Admin logs in with the username given below -->
        <admin-user>admin</admin-user>
        <admin-password>admin</admin-password>
    </authentication>

3. Start the media server

# sudo systemctl start icecast2
# sudo systemctl enable icecast2
# sudo systemctl status icecast2

icecast2.service - LSB: Icecast2 streaming media server
     Loaded: loaded (/etc/init.d/icecast2; generated)
     Active: active (running) since Mon 2026-01-26 20:39:21 GMT; 16h ago
 Invocation: efcae9e49e66447385f159ac01a307db
       Docs: man:systemd-sysv-generator(8)
      Tasks: 5 (limit: 4762)
        CPU: 25.175s
     CGroup: /system.slice/icecast2.service
             └─875 /usr/bin/icecast2 -b -c /etc/icecast2/icecast.xml

 

Transferring Audio from the Webcam to the Media Servier

1. Install darkice

# sudo apt-get install darkice

2. Configure darkice

Create a new configuration file (the installation may not do this for you).  Ensure that the device matches the device configured in (udev) above.  Set the sample rates etc. to something supported by the camera, and optionally set a low pass (highest cut-off frequency) and high pass (lowest cut-off frequency) filter.

Ensure that the password listed matches the 'source' password in the icecast.xml file.

# sudo vi /etc/icecast2/darkice.cfg

[general]
duration = 0
bufferSecs = 3
reconnect = yes

[input]
device = hw:CARD=webcam_audio,DEV=0
sampleRate = 48000
bitsPerSample = 16
channel = 1

[icecast2-0]
bitrateMode = cbr
format = mp3
bitrate = 384
quality = 0.8
channel = 1
lowpass = -1
highpass = 1500
sampleRate = 48000
server = 127.0.0.1
port = 8000
password = source
mountPoint = stream
name = Birdbox Webcam

3. Ensure that the webcam microphone gain is set to 0, and it is not muted

# alsamixer

Select device with F6

Select microphone with F4

4. Configure darkice to be launched with systemd

# sudo vi /etc/systemd/system/darkice.service

[Unit]
Description=Darkice Live Audio Streamer
After=network.target
StartLimitIntervalSec=0
StartLimitBurst=1


[Service]
ExecStart=/usr/bin/darkice -c /etc/icecast2/darkice.cfg
Restart=always
RestartSec=15s
User=root

[Install]
WantedBy=multi-user.target

The StartIntervalSec=0 means that the service restart will be re-tried indefinately.

Restart attempts will be every 15 seconds until successful.  As no dependency is specified, the first start will fail (until the icecast2 server comes online), but this is automatically resolved by the restart attempt.

5. Start darkice 

# sudo systemctl start darkice
# sudo systemctl enable darkice
# sudo systemctl status darkice

darkice.service - Darkice Live Audio Streamer
     Loaded: loaded (/etc/systemd/system/darkice.service; enabled; preset: enabled)
     Active: active (running) since Mon 2026-01-26 20:46:09 GMT; 16h ago
 Invocation: 86117aad9e3c45dcb2b40c561a8aa0b2
   Main PID: 1297 (darkice)
      Tasks: 2 (limit: 4762)
        CPU: 31min 49.791s
     CGroup: /system.slice/darkice.service

             └─1297 /usr/bin/darkice -c /etc/icecast2/darkice.cfg

Check Everything is OK

Visit http://your-camera-server:8081
You should see a shot from the camera itself

Visit: http://your-camera-server:8000
Log-in as admin/admin (match the setting in icecast.xml)
You should see 2 sections:
Global Server Stats - check that "sources" = 1
Mountpoint /stream - check that it exists, and the "server_name" matches the name set in darkice.cfg

Click on the M3U file on the Mountpoint /stream section, and open it in a media player such as VLC
You should hear the audio from the microphone

Editing and Tweaking


If you edit the darkice configuration:
# sudo systemctl restart darkice

If you edit the icecast2 configuration
# sudo systemctl restart icecast2

If you edit the motion configuration
# sudo systemctl restart motion




Tuesday, 25 November 2025

Re-connecting Home Assistant to Google Home

 These steps assume that the Google Home has been previously paired with Home Assistant, and the settings in the Google Cloud API pages are correct; the Home Assistant has been installed and is accessible to the internet via a https page; and the users are correctly set up in Home Assistant.

1. In The Google Home App, select  your Google Account icon, and 'Home Settings'

2. Select 'Works with Google'

3. Select the '[test] Home Assistant'

4. Select 'Reconnect account'

5. On the 'Link and account' page, select 'Continue'

6. On the next page, select 'Agree and continue'

You will then be taken to your login page for Home Assistant.  DO NOT put your username and password in on this page.

7. in the '...' menu on the top right, select 'Open in Chrome Browser'

8. Now put in your username and password

9. Finally, select 'Open With ... Google Home'





Friday, 11 July 2025

Raspberry Pi Postfix Configuration -> Forward all Email via Ionos

This note presents a method of setting up ionos as a smart email relay for a registered domain (in this example, one called ionos.domain).


1. Set up Ionos and Get Details

You will need to have already set up your ionos mailbox, and bound it to one of your domain addresses.

You will also need to know the password to access your mailbox.

You can set these up in the Ionos Email configuration pages - just find your desired mailbox, and select Settings / Change Password etc. from the '...' drop-down menu.


2. Install Postfix

On the raspberry Pi, make sure that postfix is installed.  You may also need to remove sendmail if it has been previously installed.

$ sudo apt-get remove sendmail

$ sudo apt-get install postfix


3. Edit /etc/postfix/main.cf

  • Add a section for the ionos server (relayhost etc.)
  • Add a link to the password details for ionos (sasl_passwd)
  • Add a virtual aliases link for mail forwarding for local users

$ sudo vi /etc/postfix/main.cf


# Don't handle anything locally

# mydestination = ionos.domain, $myhostname,  localhost.localdomain, localhost


# Setup Ionos relay

relayhost = smtp.ionos.co.uk:587

smtp_sasl_auth_enable = yes

smtp_sasl_password_maps =  hash:/etc/postfix/sasl_passwd

smtp_sasl_security_options = noanonymous  

smtp_use_tls = yes


# Setup email forwarding

virtual_alias_maps = hash:/etc/postfix/virtual


4. Add your access details

 $ sudo vi /etc/postfix/sasl_passwd

smtp.ionos.co.uk mailbox@ionos.domain:mailbox-password

 

$ sudo chmod 600 /etc/postfix/sasl-passwd

$ sudo postmap /etc/postfix/sasl-passwd


5. Create Mail Forwarding for Local Users

 $ sudo vi /etc/postfix/virtual

asterisk forward1

admin forward1


root forward2


forward1 remoteuser1@gmail.com

forward2 remoteuser2@gmail.com

 

$ sudo chmod 644 /etc/postfix/sasl-passwd

$ sudo postmap /etc/postfix/sasl-passwd


6. Re-start postfix

$ sudo systemctl restart postfix


7. Check Postfix Logs

journalctl -u postfix@-.service




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.







Connecting Silvercrest SBF75 Scales to An Android Phone using OpenScales and Google Fit

Overview This guide includes the steps to connect a Silvercrest SBF75 set of scales to Google Fit on an Android Phone using the OpenScales a...