A system for playing gong sounds in multiple locations at meditation centers. Managed using balenaCloud. Can play manually by pressing a button or automatically by fetching course schedule from dhamma.org.
- Device types
- Architecture
- Communication
- Configuration
- Automation
- Web interface
- Development
- Deployment
The system consists of 3 device types:
Has a MQTT server and contains logic for when and where gong should be played.
Using balena audio block for access to the audio hardware on the device and a nodejs service for playing sound to the audio block when a message is recieved from the server.
Connected to an amplifier with speakers.
Has a button connected on GPIO pins of the device. Sends a message to the server when the button is activated or inactivated. Shows current status of playing from the server using a LED.
Code will be written in nodejs using TypeScript. Some existing code is for now just JavaScript.
With the help of balena the application is running in Docker containers.
Configuration for the MQTT container.
Prebuilt image with a MQTT server.
The server logic. Communicates with players and remotes.
Prebuilt image by balena that handles audio playback. Sound data is sent to this container using a pulse socket.
The software that plays a sound file and ouputs the sound to the pulse socket for the audio container to pick up. Communicates with the server.
Reads a GPIO pin for button presses and writes to a GPIO pin for giving feedback using a LED. Communicates with the server.
MQTT message broker is used for communication between the different devices. If data is needed it is in JSON format.
Name of device in lowercase and dashes instead of space.
Ending with a dash and the device type: -remote, -player
Configured with device variable NAME in balenaCloud dashboard.
Example names:
- main-house-remote
- main-house-player
- female-house-player
- male-house-player
- staff-house-player
- dhamma-hall-player
Which locations should be affected by a play message.
Configured with device variable LOCATIONS on a player in balenaCloud dashboard.
- all - Play everywhere. This should not be used in early mornings to not disturb neighbours by playing outside or waking up staff that are sleeping.
- student-accommodation - Speakers in student accommodation houses. Should be played every time.
- staff-accommodation - Speakers in staff accommodation houses. Should not be played in the early mornings.
- outside - Speakers mounted on the outside of the main house and the Dhamma hall. Should not be played in early mornings.
Request devices to send their status by publishing a pong message.
Published by the server once every 60 seconds.
Send a message telling that the device is online. Sent when device has booted and as a response to ping message.
- name: string - Name of the device to easily identify it.
- locations: array of locations handled by the player. (player)
- type: string - Device type: remote, player
- status: string - optional - ok, warning, failed, disabled
Example data:
{"name": "main-house-remote", "type": "remote", "status": "disabled"}
{"name": "female-house-player", "locations": ["student-accommodation"], "type": "player"}
{"name": "main-house-player", "locations": ["student-accommodation", "outside"], "type": "player"}
Play gong sound if player is configured to handle the zone requested.
- type: string referring to sound type on player, usually 'gong'.
- locations: array of locations.
- repeat: number of times sound should be played.
Example data:
{ "type": "gong", "locations": ["all"], "repeat": 6 }
{ "type": "gong", "locations": ["student-accommodation"], "repeat": 6 }
{ "type": "gong", "locations": ["student-accommodation", "outside"], "repeat": 4 }
Report that playback has started.
- name: string - Name of the device.
Example data:
{"name": "female-house-player"}
Sent after gong has been played with this data:
- name: string. The name of the device.
- locations: array of locations player played in.
Example data:
{"name": "female-house-player", "locations": ["student-accommodation"]}
{"name": "main-house-player", "locations": ["student-accommodation", "outside"]}
Stop playback and update state of remotes to show that gong is not playing anymore.
Sent by remote when button has been pressed.
- name: string. Name of device that initiated the request.
Example data:
{"name": "main-house-remote"}
Configuration is set using fleet or device variables in balenaCloud dashboard.
Maximum audio ouput volume of the audio block in percentage.
Default: 100
Audio volume when starting playback.
Default: 50
How player application and audio block communicates.
Default: unix:/run/pulse/pulseaudio.socket
Set to true to make one device temporarly disabled. Will make any actions to have no effect.
Default: false
IP address or hostname of server.
Name of the device, used for identification.
Comma separate locations this player handles.
Example: student-accommodation,outside
Time in format hh:mm
.
If server recieves an activated message from a remote before this time, only play in zone student-accommodation.
Comma separated list of devices that should be online. Used to check status of devices.
How many times a gong should be played in a row by default. Time table entries can override this value.
If automatic fetching and parsing of courses from dhamma.org site should be used.
Default: false
If automation is enabled, id of location to fetch courses for.
Find by going to Locations, Worldwide Directory and expand a center location, choose to use developer tools to inspect the location and find the data-id
property.
Format: nnnn
Optional Data Source Name for error tracking using Sentry.
The system can fetch a centers schedule for automatic plying of gong. Locally stored time table definitions are used to transform this data into a time table for playing gong.
Files are stored in server/server/resources/timetable
in JSON format. File name is <raw_course_type>.json
where <raw_course_type> is from the fetched schedule.
See examples further down for full examples.
There are two special time table definition files: default.json
and unknown.json
default.json will be used for periods when no schedule is defined, for example between courses.
unknown.json will be used when schedule is defined but no definition exitst for that course type. This is currently set to not play any gongs at all.
Format: hh:mm
What time on the last day the course finishes. If set, gong from the next course will not be scheduled before this time.
Example: A 10 day course followed by a service period. On day 11, the closing day, of a 10 day course morning wakeup gongs are played at 4:00 and 4:20 and a Service period has gong for morning group sitting at 7:20. If the 10 day course definition has endTime set to 09:00 the morning group sitting from the Service Period will be ignored and the next gong will be at 14:20.
Gongs on closing day of a 10 day course:
- 04:00
- 04:20
- 14:20
- 19:20
An object with either default
or course day numbers as key. Value is an array of time table entries, see below.
If no key with the current course day is found, default will be used.
Example:
"0" : [],
"default" : [...]
"4": [...]
On day 0, the opening day, of a course no gongs will be played. On day 1-3 the default gongs for the course will be played. On day 4 the gong defined for day 4 will be played.
Format: hh:mm
What time gong should be played.
What type of gong sound to be played. Currently always set to gong
.
An array of locations where the gong shoule be played. See Location.
How many times a gong should be played. If no value is defined the GONG_REPEAT value will be used.
Example 1:
{ "time": "13:00", "type": "gong", "location": ["student-accommodation", "outside"] }
Example 2:
{ "time": "04:00", "type": "gong", "location": ["student-accommodation"], "repeat": 8 }
{
"definition" : {
"endTime": "08:50"
},
"days" : {
"0" : [
{ "time": "07:20", "type": "gong", "location": ["all"] },
{ "time": "12:50", "type": "gong", "location": ["all"] }
],
"default" : [
{ "time": "04:00", "type": "gong", "location": ["student-accommodation"], "repeat": 8 },
{ "time": "04:20", "type": "gong", "location": ["student-accommodation"], "repeat": 8 },
{ "time": "07:48", "type": "gong", "location": ["all"] },
{ "time": "12:50", "type": "gong", "location": ["student-accommodation", "outside"] },
{ "time": "14:15", "type": "gong", "location": ["student-accommodation", "outside"] },
{ "time": "14:23", "type": "gong", "location": ["all"] },
{ "time": "17:48", "type": "gong", "location": ["all"] }
],
"4" : [
{ "time": "04:00", "type": "gong", "location": ["student-accommodation"], "repeat": 8 },
{ "time": "04:20", "type": "gong", "location": ["student-accommodation"], "repeat": 8 },
{ "time": "07:48", "type": "gong", "location": ["all"] },
{ "time": "12:50", "type": "gong", "location": ["student-accommodation", "outside"] },
{ "time": "13:50", "type": "gong", "location": ["all"] },
{ "time": "17:48", "type": "gong", "location": ["all"] }
],
"10" : [
{ "time": "04:00", "type": "gong", "location": ["student-accommodation"], "repeat": 8 },
{ "time": "04:20", "type": "gong", "location": ["student-accommodation"], "repeat": 8 },
{ "time": "07:48", "type": "gong", "location": ["all"] },
{ "time": "14:20", "type": "gong", "location": ["all"] },
{ "time": "15:55", "type": "gong", "location": ["all"] },
{ "time": "17:48", "type": "gong", "location": ["all"] }
],
"11" : [
{ "time": "04:00", "type": "gong", "location": ["student-accommodation"], "repeat": 8 },
{ "time": "04:20", "type": "gong", "location": ["student-accommodation"], "repeat": 8 },
{ "time": "08:50", "type": "gong", "location": ["all"] }
]
}
}
{
"days": {
"default": [
{ "time": "07:20", "type": "gong", "location": ["all"] },
{ "time": "14:20", "type": "gong", "location": ["all"] },
{ "time": "19:20", "type": "gong", "location": ["all"] }
]
}
}
A basic web interface is hosted on the server device and available to check system status and enabling or disabling system and automation.
For running the project using docker on the developement machine:
docker-compose -f docker-compose.development.yml up --build
For running all services on a single device in local mode using balenaCloud:
balena push <device_ip>
Or to run all services on a single device deployed to gong/development on balenaCloud:
balena push gong/development
Deployment to balenaCloud and devices is done from subfolders for each device type.
cd server
balena push gong/server
cd player
balena push gong/player
cd remote
balena push gong/remote