"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
-<!-- 2024-09-03 Tue 08:46 -->
+<!-- 2024-09-20 Fri 13:28 -->
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Birchwood Abbey Networks</title>
philosophy, attitude.
</p>
-<pre class="example" id="org666ada7">
+<pre class="example" id="orgff81049">
|
=
_|||_
Dovecot-IMAPd, and hosting a VPN with OpenVPN.
</p>
</div>
-<div id="outline-container-orgbdc000c" class="outline-3">
-<h3 id="orgbdc000c"><span class="section-number-3">3.1.</span> Install Emacs</h3>
+<div id="outline-container-org5cff3f5" class="outline-3">
+<h3 id="org5cff3f5"><span class="section-number-3">3.1.</span> Install Emacs</h3>
<div class="outline-text-3" id="text-3-1">
<p>
The monks of the abbey are masters of the staff (bo) and Emacs.
entered as shown below).
</p>
-<pre class="example" id="org39bc164">
+<pre class="example" id="orgc807cc9">
$ sudo apt install python3-certbot-apache
$ sudo certbot --apache -d birchwood-abbey.net
...
NTP, DNS and DHCP.
</p>
</div>
-<div id="outline-container-orgf7f2984" class="outline-3">
-<h3 id="orgf7f2984"><span class="section-number-3">4.1.</span> Include Abbey Variables</h3>
+<div id="outline-container-orge3238f6" class="outline-3">
+<h3 id="orge3238f6"><span class="section-number-3">4.1.</span> Include Abbey Variables</h3>
<div class="outline-text-3" id="text-4-1">
<p>
In this abbey specific document, most abbey particulars are not
<h3 id="org001474c"><span class="section-number-3">4.2.</span> Install Additional Packages</h3>
<div class="outline-text-3" id="text-4-2">
<p>
-The scripts that maintain the abbey's web site and run the Weather
-project use a number of additional software packages. The
-<q>/WWW/live/Private/make-top-index</q> script uses <code>HTML::TreeBuilder</code> in
-the <code>libhtml-tree-perl</code> package. The house task list uses JQuery.
-Weather scripts use <code>mit-scheme</code> and <code>gnuplot</code> (in pseudonymous
-packages).
+The scripts that maintain the abbey's web site use a number of
+additional software packages. The <q>/WWW/live/Private/make-top-index</q>
+script uses <code>HTML::TreeBuilder</code> in the <code>libhtml-tree-perl</code> package.
+The house task list uses JQuery.
</p>
<div class="org-src-container">
</div>
</div>
</div>
-<div id="outline-container-org1a716c3" class="outline-3">
-<h3 id="org1a716c3"><span class="section-number-3">4.8.</span> Use Cloister Apt Cache</h3>
+<div id="outline-container-orga5b3bf9" class="outline-3">
+<h3 id="orga5b3bf9"><span class="section-number-3">4.8.</span> Use Cloister Apt Cache</h3>
<div class="outline-text-3" id="text-4-8">
<p>
-Core itself will benefit from using the package cache.
+Core itself will benefit from using the package cache, but should
+contact <code>https</code> repositories directly. (There are few such cretins
+so caching their packages is not a priority.)
</p>
<div class="org-src-container">
content: >
Acquire::http::Proxy
<span class="org-string">"http://apt-cacher.birchwood.private.:3142"</span>;
+
+ Acquire::https::Proxy <span class="org-string">"DIRECT"</span>;
dest: /etc/apt/apt.conf.d/01proxy
<span class="org-variable-name">mode: u</span>=rw,g=r,o=r
</pre>
<a href="roles_t/abbey-core/tasks/main.yml"><q>roles_t/abbey-core/tasks/main.yml</q></a><pre class="src src-conf">
- name: Install Munin.
become: yes
- apt:
- pkg: munin
+ <span class="org-variable-name">apt: pkg</span>=munin
-- name: Add {{ ansible_user }} to Munin group.
+- name: Add {{ ansible_user }} to munin group.
become: yes
user:
name: <span class="org-string">"{{ ansible_user }}"</span>
</div>
</div>
</div>
-<div id="outline-container-org9a9dc68" class="outline-3">
-<h3 id="org9a9dc68"><span class="section-number-3">4.17.</span> Configure Weather Updates</h3>
+<div id="outline-container-org248a7c3" class="outline-3">
+<h3 id="org248a7c3"><span class="section-number-3">4.17.</span> Install Samba</h3>
<div class="outline-text-3" id="text-4-17">
<p>
-Monkey on Core runs <q>/WWW/campus/Weather/Private/cronjob</q> every 5
-minutes and <q>cronjob-midnight</q> at midnight.
+The abbey core provides NAS (Network Attached Storage) service to the
+cloister network. It also provides writable shares for a Home
+Assistant appliance (Raspberry Pi).
</p>
+<ul class="org-ul">
+<li>Install <code>samba</code>.</li>
+<li>Create system user <code>hass</code>.</li>
+<li>Create <q>/home/hass/{media,backup,share}/</q> with appropriate
+permissions.</li>
+</ul>
+
<div class="org-src-container">
-<a href="roles_t/abbey-core/tasks/main.yml"><q>roles_t/abbey-core/tasks/main.yml</q></a><pre class="src src-:tangle">
-- name: Create Monkey's weather job.
+<a href="roles_t/abbey-core/tasks/main.yml"><q>roles_t/abbey-core/tasks/main.yml</q></a><pre class="src src-conf">
+- name: Install Samba.
+ become: yes
+ <span class="org-variable-name">apt: pkg</span>=samba
+
+- name: Add system user hass.
+ become: yes
+ user:
+ name: hass
+ system: yes
+
+- name: Add {{ ansible_user }} to hass group.
+ become: yes
+ user:
+ name: <span class="org-string">"{{ ansible_user }}"</span>
+ append: yes
+ groups: hass
+
+- name: Configure shares.
become: yes
- cron:
- name: weather
- hour: "*"
- minute: "*/5"
- job: "[ -d /WWW/house ] && /WWW/house/Weather/Private/cronjob"
- user: monkey
+ blockinfile:
+ block: |
+ [<span class="org-type">Shared</span>]
+ <span class="org-variable-name">path</span> = /Shared
+ <span class="org-variable-name">guest ok</span> = yes
+ <span class="org-variable-name">read only</span> = yes
+
+ [<span class="org-type">HASS-backup</span>]
+ <span class="org-variable-name">comment</span> = Home Assistant backup
+ <span class="org-variable-name">path</span> = /home/hass/backup
+ <span class="org-variable-name">valid users</span> = hass
+ <span class="org-variable-name">read only</span> = no
+
+ [<span class="org-type">HASS-media</span>]
+ <span class="org-variable-name">comment</span> = Home Assistant media
+ <span class="org-variable-name">path</span> = /home/hass/media
+ <span class="org-variable-name">valid users</span> = hass
+ <span class="org-variable-name">read only</span> = yes
+
+ [<span class="org-type">HASS-share</span>]
+ <span class="org-variable-name">comment</span> = Home Assistant share
+ <span class="org-variable-name">path</span> = /home/hass/share
+ <span class="org-variable-name">valid users</span> = hass
+ <span class="org-variable-name">read only</span> = no
+ dest: /etc/samba/smb.conf
+ marker: <span class="org-string">"# {mark} ABBEY MANAGED BLOCK"</span>
+ notify: New shares.
+</pre>
+</div>
+
+<div class="org-src-container">
+<a href="roles_t/abbey-core/handlers/main.yml"><q>roles_t/abbey-core/handlers/main.yml</q></a><pre class="src src-conf">
+- name: New shares.
+ become: yes
+ systemd:
+ service: smbd
+ state: reloaded
</pre>
</div>
</div>
./abbey client campus new-host-name
</pre>
</div>
-<div id="outline-container-orga5b3bf9" class="outline-3">
-<h3 id="orga5b3bf9"><span class="section-number-3">6.1.</span> Use Cloister Apt Cache</h3>
+<div id="outline-container-orgedd1215" class="outline-3">
+<h3 id="orgedd1215"><span class="section-number-3">6.1.</span> Use Cloister Apt Cache</h3>
<div class="outline-text-3" id="text-6-1">
<p>
The Apt-Cacher:TNG program does not work well on the frontier, so is
while.
</p>
+<p>
+Again, <code>https</code> repositories are contacted directly, cached only on the
+local host.
+</p>
+
<div class="org-src-container">
<a href="roles_t/abbey-cloister/tasks/main.yml"><q>roles_t/abbey-cloister/tasks/main.yml</q></a><pre class="src src-conf">---
- name: Use the local Apt package cache.
content: >
Acquire::http::Proxy
<span class="org-string">"http://apt-cacher.birchwood.private.:3142"</span>;
+
+ Acquire::https::Proxy <span class="org-string">"DIRECT"</span>;
dest: /etc/apt/apt.conf.d/01proxy
<span class="org-variable-name">mode: u</span>=rw,g=r,o=r
</pre>
<a href="roles_t/abbey-cloister/tasks/main.yml"><q>roles_t/abbey-cloister/tasks/main.yml</q></a><pre class="src src-conf">
- name: Install Munin Node.
become: yes
- apt:
- pkg: munin-node
+ <span class="org-variable-name">apt: pkg</span>=munin-node
-- name: Add {{ ansible_user }} to Munin group.
+- name: Add {{ ansible_user }} to munin group.
become: yes
user:
name: <span class="org-string">"{{ ansible_user }}"</span>
</div>
</div>
</div>
-<div id="outline-container-org5cff3f5" class="outline-3">
-<h3 id="org5cff3f5"><span class="section-number-3">6.4.</span> Install Emacs</h3>
+<div id="outline-container-org832124f" class="outline-3">
+<h3 id="org832124f"><span class="section-number-3">6.4.</span> Install Emacs</h3>
<div class="outline-text-3" id="text-6-4">
<p>
The monks of the abbey are masters of the staff and Emacs.
<h2 id="org7341dda"><span class="section-number-2">7.</span> The Abbey Weather Role</h2>
<div class="outline-text-2" id="text-7">
<p>
-Birchwood Abbey's weather hosts use the 1-Wire server (from the
-<code>owserver</code> package) and a 1-Wire USB adapter. They use an
-unprivileged account (<code>monkey</code>) to run a SystemD service named
-<code>weatherd</code> (aka "the daemon"). The daemon is a Perl script that runs
-<code>owread</code> and logs the new measurements once per minute.
-</p>
-
-<p>
-The log files are collected by Monkey on Core (via <code>rsync</code>), then
-processed and published in campus web pages by The Weather Project's
-code (old, using <code>gnuplot(1)</code>, and so… unpublished).
-</p>
-</div>
-<div id="outline-container-org6925511" class="outline-3">
-<h3 id="org6925511"><span class="section-number-3">7.1.</span> The Abbey Weather Hardware</h3>
-<div class="outline-text-3" id="text-7-1">
-<p>
-The abbey currently has one weather host, Gate, and a couple 1-Wire
-sensor modules. The modules measure inside and outside temperature
-and humidity. Their desired locations are 7-8m from the core servers
-so they are plugged into a custom Y cable, with the inside sensor
-cable spliced into the middle of the outside/main cable. The proximal
-end's RJ11 plugs into a 1-Wire USB adapter (a DS9490R) plugged into
-Gate. The outside end goes out the window with the Starlink cable.
-</p>
-</div>
-</div>
-<div id="outline-container-org0b52261" class="outline-3">
-<h3 id="org0b52261"><span class="section-number-3">7.2.</span> The Abbey Weather Host Setup</h3>
-<div class="outline-text-3" id="text-7-2">
-<p>
-The Ansible code in the <code>abbey-weather</code> role assumes it is working
-with a cloistered host (as described in <a href="#org110d7b3">Cloistering</a>) and proceeds in
-two phases. The first installs the <code>ow-server</code> package and configures
-it to use a DS9490 (USB adapter) rather than a debugging fake. After
-the first <code>./abbey config new</code>, the new weather host seems to need a
-reboot before the 1-Wire bus becomes visible via <code>owdir</code>.
+Birchwood Abbey now uses Home Assistant to record and display weather
+data from an Ecowitt GW2001 IoT gateway connecting wirelessly to a
+WS90 (7 function weather station) and a couple WN31s (temp/humidity
+sensors).
</p>
<p>
-After a reboot <code>owdir</code> should list one or more type 26 device IDs.
-Listing them (e.g. running <code>owdir /26.nnnnnnnn</code> or <code>owdir
-/26.nnnnnnnn/HIH</code>) should reveal "files" named <q>temperature</q> and
-<q>HIH/humidity</q>. These pseudo-file paths are used in the daemon script
-below. A test session is shown below.
+The configuration of the GW2001 IoT hub involved turning off the Wi-Fi
+access point, and disabling unused channels. The hub reports the data
+from all sensors in range, <i>anyone's</i> sensors. These new data sources
+are noticed and recorded by Home Assistant automatically as similarly
+equipped campers come and go. Disabling unused channels helps avoid
+these distractions.
</p>
-<pre class="example" id="orgd2ab943">
-monkey@new$ owdir
-...
- /26.2153B6000000/
-...
-monkey@new$ owdir /26.2153B6000000
-...
- /26.2153B6000000/temperature
-...
-monkey@new$ owread /26.2153B6000000/temperature; echo
-26.125
-monkey@new$
-</pre>
-
<p>
-The second phase of weather host configuration waits for the host-
-specific weather daemon script to appear in the role's <q>files/</q>.
+The configuration of Home Assistant involved installing the Ecowitt
+"integration". This was accomplished by choosing "Settings", then
+"Devices & services", then "Add Integration", and searching for
+"Ecowitt". Once installed, the integration created dozens of weather
+entities which were organized into an "Abbey" dashboard.
</p>
</div>
</div>
-<div id="outline-container-org6ea9cdd" class="outline-3">
-<h3 id="org6ea9cdd"><span class="section-number-3">7.3.</span> The Abbey Weather Daemons</h3>
-<div class="outline-text-3" id="text-7-3">
+<div id="outline-container-org2c65dbc" class="outline-2">
+<h2 id="org2c65dbc"><span class="section-number-2">8.</span> The Abbey DVR Role</h2>
+<div class="outline-text-2" id="text-8">
<p>
-Different weather hosts, with different 1-Wire devices, need different
-daemon scripts, to call <code>owread</code> with different paths (containing the
-IDs of each host's devices). At the moment there is just the
-one weather host, <code>anoat</code>.
+The abbey uses AgentDVR to record video from PoE IP HD security
+cameras. The "download" button on iSpy's Download page
+(<a href="https://www.ispyconnect.com/download">https://www.ispyconnect.com/download</a>), when "Agent DVR - Linux/
+macOS/ RPi" is chosen, suggests the following command lines (the
+second of which is broken across three lines).
</p>
<div class="org-src-container">
-<a href="roles_t/abbey-weather/files/daemon-anoat"><q>roles_t/abbey-weather/files/daemon-anoat</q></a><pre class="src src-perl"><span class="org-comment-delimiter">#</span><span class="org-comment">!/usr/bin/perl -w</span>
-<span class="org-comment-delimiter"># </span><span class="org-comment">-*- CPerl -*-</span>
-<span class="org-comment-delimiter">#</span>
-<span class="org-comment-delimiter"># </span><span class="org-comment">Weather/daemon</span>
-<span class="org-comment-delimiter">#</span>
-<span class="org-comment-delimiter"># </span><span class="org-comment">Fetches data from the local owserver once per minute. Appends to</span>
-<span class="org-comment-delimiter"># </span><span class="org-comment">Log/{In,Out}side/YEAR/MONTH/DAY.txt.</span>
-
-<span class="org-keyword">use</span> <span class="org-constant">strict</span>;
-<span class="org-keyword">use</span> <span class="org-constant">IO::File</span>;
-<span class="org-keyword">use</span> <span class="org-constant">Date::Format</span>;
-
-<span class="org-keyword">my</span> $<span class="org-variable-name">ILOG</span>;
-<span class="org-keyword">my</span> $<span class="org-variable-name">OLOG</span>;
-<span class="org-keyword">my</span> $<span class="org-variable-name">ymd</span> = <span class="org-string">""</span>;
-<span class="org-keyword">sub</span> <span class="org-function-name">mymkdir</span> ($);
-<span class="org-keyword">sub</span> <span class="org-function-name">reopen_logs</span> ()
-{
- <span class="org-keyword">my</span> $<span class="org-variable-name">time</span> = time;
- <span class="org-keyword">my</span> $<span class="org-variable-name">datime</span> = time2str (<span class="org-string">"%Y-%m-%d %H:%M:%S"</span>, $<span class="org-variable-name">time</span>, <span class="org-string">"UTC"</span>);
- <span class="org-keyword">my</span> ($<span class="org-variable-name">year</span>, $<span class="org-variable-name">month</span>, $<span class="org-variable-name">day</span>) = $<span class="org-variable-name">datime</span> =~ <span class="org-string">/^(\d{4})-(\d\d)-(\d\d) /</span>;
- <span class="org-keyword">my</span> $<span class="org-variable-name">new_ymd</span> = <span class="org-string">"$year/$month/$day"</span>;
- <span class="org-keyword">return</span> <span class="org-keyword">if</span> $<span class="org-variable-name">new_ymd</span> eq $<span class="org-variable-name">ymd</span>;
- close $<span class="org-variable-name">ILOG</span> <span class="org-keyword">if</span> defined $<span class="org-variable-name">ILOG</span>;
- close $<span class="org-variable-name">OLOG</span> <span class="org-keyword">if</span> defined $<span class="org-variable-name">OLOG</span>;
- umask 07;
- mymkdir <span class="org-string">"Inside/$year/$month"</span>;
- mymkdir <span class="org-string">"Outside/$year/$month"</span>;
- umask 027;
- <span class="org-keyword">my</span> $<span class="org-variable-name">filename</span> = <span class="org-string">"Inside/$new_ymd.txt"</span>;
- $<span class="org-variable-name">ILOG</span> = new IO::File;
- open $<span class="org-variable-name">ILOG</span>, <span class="org-string">">>$filename"</span> or <span class="org-keyword">die</span> <span class="org-string">"Could not open $filename: $!\n"</span>;
- $<span class="org-variable-name">filename</span> = <span class="org-string">"Outside/$new_ymd.txt"</span>;
- $<span class="org-variable-name">OLOG</span> = new IO::File;
- open $<span class="org-variable-name">OLOG</span>, <span class="org-string">">>$filename"</span> or <span class="org-keyword">die</span> <span class="org-string">"Could not open $filename: $!\n"</span>;
- $<span class="org-variable-name">ymd</span> = $<span class="org-variable-name">new_ymd</span>;
-}
-
-<span class="org-keyword">sub</span> <span class="org-function-name">logit</span> ($$$);
-<span class="org-keyword">sub</span> <span class="org-function-name">main</span> () {
- <span class="org-keyword">die</span> <span class="org-string">"usage: $0\n"</span> <span class="org-keyword">if</span> @<span class="org-perl-non-scalar-variable">ARGV</span> != 0;
- $<span class="org-variable-name">0</span> = <span class="org-string">"weatherd"</span>;
- chdir <span class="org-string">"/home/monkey/Weather/Log"</span> or <span class="org-keyword">die</span>;
- umask 027;
- <span class="org-keyword">my</span> $<span class="org-variable-name">start</span> = time;
- {
- <span class="org-keyword">my</span> $<span class="org-variable-name">secs</span> = 60 - $<span class="org-variable-name">start</span> % 60;
- $<span class="org-variable-name">start</span> += $<span class="org-variable-name">secs</span>;
- sleep ($<span class="org-variable-name">secs</span>);
- }
- <span class="org-keyword">while</span> (1) {
- reopen_logs;
- logit $<span class="org-variable-name">OLOG</span>, <span class="org-string">"T"</span>, <span class="org-string">"/26.2153B6000000/temperature"</span>;
- logit $<span class="org-variable-name">OLOG</span>, <span class="org-string">"H"</span>, <span class="org-string">"/26.2153B6000000/HIH4000/humidity"</span>;
- logit $<span class="org-variable-name">ILOG</span>, <span class="org-string">"T"</span>, <span class="org-string">"/26.8859B6000000/temperature"</span>;
- logit $<span class="org-variable-name">ILOG</span>, <span class="org-string">"H"</span>, <span class="org-string">"/26.8859B6000000/HIH4000/humidity"</span>;
- $<span class="org-variable-name">start</span> += 60;
- <span class="org-keyword">my</span> $<span class="org-variable-name">now</span> = time;
- <span class="org-keyword">while</span> ($<span class="org-variable-name">start</span> < $<span class="org-variable-name">now</span>) { $<span class="org-variable-name">start</span> += 60; }
- <span class="org-keyword">my</span> $<span class="org-variable-name">secs</span> = $<span class="org-variable-name">start</span> - $<span class="org-variable-name">now</span>;
- sleep ($<span class="org-variable-name">secs</span>);
- }
-}
-
-<span class="org-keyword">sub</span> <span class="org-function-name">logit</span> ($$$)
-{
- <span class="org-keyword">my</span> ($<span class="org-variable-name">log</span>, $<span class="org-variable-name">name</span>, $<span class="org-variable-name">query</span>) = @<span class="org-perl-non-scalar-variable">_</span>;
-
- <span class="org-keyword">my</span> $<span class="org-variable-name">tries</span> = 0;
- <span class="org-keyword">while</span> ($<span class="org-variable-name">tries</span> < 3) {
- <span class="org-keyword">my</span> $<span class="org-variable-name">time</span> = time;
- <span class="org-keyword">my</span> $<span class="org-variable-name">datime</span> = time2str (<span class="org-string">"%Y-%m-%d %H:%M:%S"</span>, $<span class="org-variable-name">time</span>, <span class="org-string">"UTC"</span>);
- $<span class="org-variable-name">tries</span> += 1;
- <span class="org-keyword">my</span> @<span class="org-perl-non-scalar-variable">lines</span> = <span class="org-string">`/usr/bin/owread $query`</span>;
- chomp @<span class="org-perl-non-scalar-variable">lines</span>;
- <span class="org-keyword">my</span> $<span class="org-variable-name">status</span> = $?;
- <span class="org-keyword">my</span> $<span class="org-variable-name">sig</span> = $<span class="org-variable-name">status</span> & 127;
- $<span class="org-variable-name">status</span> >>= 8;
- <span class="org-keyword">if</span> ($<span class="org-variable-name">status</span> != 0) {
- <span class="org-keyword">my</span> $<span class="org-variable-name">L</span> = join <span class="org-string">"\\n"</span>, @<span class="org-perl-non-scalar-variable">lines</span>;
- print $<span class="org-variable-name">log</span> <span class="org-string">"$datime\t$name\terror: status $status: $L\n"</span>;
- $<span class="org-variable-name">log</span>->flush;
- } <span class="org-keyword">elsif</span> (@<span class="org-perl-non-scalar-variable">lines</span> != 1) {
- <span class="org-keyword">my</span> $<span class="org-variable-name">L</span> = join <span class="org-string">"\\n"</span>, @<span class="org-perl-non-scalar-variable">lines</span>;
- print $<span class="org-variable-name">log</span> <span class="org-string">"$datime\t$name\terror: multiple lines: $L\n"</span>;
- $<span class="org-variable-name">log</span>->flush;
- } <span class="org-keyword">elsif</span> ($<span class="org-variable-name">lines</span>[0] !~ <span class="org-string">/^ *(-?\d+(\.\d+)?)$/</span>) {
- <span class="org-keyword">my</span> $<span class="org-variable-name">L</span> = $<span class="org-variable-name">lines</span>[0];
- print $<span class="org-variable-name">log</span> <span class="org-string">"$datime\t$name\terror: bogus line: $L\n"</span>;
- $<span class="org-variable-name">log</span>->flush;
- } <span class="org-keyword">else</span> {
- <span class="org-keyword">my</span> $<span class="org-variable-name">datum</span> = $<span class="org-variable-name">1</span>;
- print $<span class="org-variable-name">log</span> <span class="org-string">"$datime\t$name\t$datum\n"</span>;
- $<span class="org-variable-name">log</span>->flush;
- <span class="org-keyword">return</span>;
- }
- }
-}
-
-<span class="org-keyword">sub</span> <span class="org-function-name">mymkdir</span> ($)
-{
- <span class="org-keyword">my</span> ($<span class="org-variable-name">dirpath</span>) = @<span class="org-perl-non-scalar-variable">_</span>;
-
- <span class="org-keyword">my</span> @<span class="org-perl-non-scalar-variable">path_names</span> = split <span class="org-string">/\//</span>, $<span class="org-variable-name">dirpath</span>;
- <span class="org-keyword">my</span> $<span class="org-variable-name">path</span>;
- <span class="org-keyword">if</span> (!$<span class="org-variable-name">path_names</span>[0]) {
- $<span class="org-variable-name">path</span> = <span class="org-string">"/"</span>;
- shift @<span class="org-perl-non-scalar-variable">path_names</span>;
- } <span class="org-keyword">else</span> {
- $<span class="org-variable-name">path</span> = <span class="org-string">"."</span>;
- }
- <span class="org-keyword">my</span> @<span class="org-perl-non-scalar-variable">created</span>;
- <span class="org-keyword">while</span> (@<span class="org-perl-non-scalar-variable">path_names</span>) {
- $<span class="org-variable-name">path</span> .= <span class="org-string">"/"</span> . shift @<span class="org-perl-non-scalar-variable">path_names</span>;
- <span class="org-keyword">if</span> (! -d $<span class="org-variable-name">path</span>) {
- <span class="org-keyword">if</span> (-e $<span class="org-variable-name">path</span>) {
- <span class="org-keyword">die</span> <span class="org-string">"mkdir $dirpath: already exists; not a directory!\n"</span>;
- }
- <span class="org-keyword">if</span> (! mkdir $<span class="org-variable-name">path</span>) {
- <span class="org-keyword">die</span> <span class="org-string">"mkdir $path: $!\n"</span>;
- } <span class="org-keyword">else</span> {
- chmod 02775, $<span class="org-variable-name">path</span>;
- push @<span class="org-perl-non-scalar-variable">created</span>, $<span class="org-variable-name">path</span>;
- }
- }
- }
- <span class="org-keyword">return</span> @<span class="org-perl-non-scalar-variable">created</span>;
-}
-
-main;
+<pre class="src src-sh">sudo apt-get install curl
+bash <(curl -s <span class="org-string">"https://raw.githubusercontent.com/\</span>
+<span class="org-string">ispysoftware/agent-install-scripts/main/v2/\</span>
+<span class="org-string">install.sh"</span>)
</pre>
</div>
<p>
-The above Perl script uses the <code>Date::Format</code> module, which is
-installed by the following task.
+Ansible assists by creating the system user <code>agentdvr</code> and granting it
+enough <code>sudo</code> latitude to run the installer as instructed above.
+Though a system user, the account gets a home directory,
+<q>/home/agentdvr/</q> in which to do the installation. The rest of the
+DVR role, "phase two", waits until AgentDVR is installed.
</p>
-<div class="org-src-container">
-<a href="roles_t/abbey-weather/tasks/main.yml"><q>roles_t/abbey-weather/tasks/main.yml</q></a><pre class="src src-conf">---
-- name: Install weather daemon packages.
- become: yes
- <span class="org-variable-name">apt: pkg</span>=libtimedate-perl
-</pre>
-</div>
-</div>
-</div>
-<div id="outline-container-orgfe87cd1" class="outline-3">
-<h3 id="orgfe87cd1"><span class="section-number-3">7.4.</span> Install 1-Wire Server</h3>
-<div class="outline-text-3" id="text-7-4">
<p>
-This next task installs the 1-Wire server and shell commands. The
-abbey uses the Dallas Semiconductor DS9490R, a USB to 1-Wire adapter,
-on all its weather hosts, so it also configures the server to use the
-USB adapter (rather than a test "fake" adapter).
+AgentDVR is installed, after Ansible has set things up, by running the
+command lines prescribed by iSpy while logged in as <code>agentdvr</code> with
+the current default directory <q>/home/agentdvr/</q>. The installer should
+create the <q>/home/agentdvr/AgentDVR/</q> directory. Its offer to install
+a system service is declined.
</p>
-<div class="org-src-container">
-<a href="roles_t/abbey-weather/tasks/main.yml"><q>roles_t/abbey-weather/tasks/main.yml</q></a><pre class="src src-conf">
-- name: Install 1-Wire server.
- become: yes
- apt:
- pkg: [ owserver, ow-shell ]
-
-- name: Configure 1-Wire server.
- become: yes
- lineinfile:
- path: /etc/owfs.conf
- regexp: <span class="org-string">"{{ item.regexp }}"</span>
- line: <span class="org-string">"{{ item.line }}"</span>
- backrefs: yes
- loop:
- - { regexp: <span class="org-string">'^[# ]*server: *FAKE(.*)$'</span>, line: <span class="org-string">'#server: FAKE\1'</span> }
- - { regexp: <span class="org-string">'^[# ]*server: *usb(.*)$'</span>, line: <span class="org-string">'server: usb\1'</span> }
-</pre>
-</div>
-</div>
-</div>
-<div id="outline-container-org816673b" class="outline-3">
-<h3 id="org816673b"><span class="section-number-3">7.5.</span> Install Rsync</h3>
-<div class="outline-text-3" id="text-7-5">
<p>
-Monkey on Core will want to download log records (files) using
-<code>rsync(1)</code>.
+After AgentDVR is installed, when the <q>/home/agentdvr/AgentDVR/</q>
+directory exists, Ansible is run again to install the system service.
</p>
-
-<div class="org-src-container">
-<a href="roles_t/abbey-weather/tasks/main.yml"><q>roles_t/abbey-weather/tasks/main.yml</q></a><pre class="src src-conf">
-- name: Install Rsync.
- become: yes
- <span class="org-variable-name">apt: pkg</span>=rsync
-</pre>
</div>
-</div>
-</div>
-<div id="outline-container-org13dabcd" class="outline-3">
-<h3 id="org13dabcd"><span class="section-number-3">7.6.</span> Create Monkey</h3>
-<div class="outline-text-3" id="text-7-6">
+<div id="outline-container-orgb219961" class="outline-3">
+<h3 id="orgb219961"><span class="section-number-3">8.1.</span> Create User <code>agentdvr</code></h3>
+<div class="outline-text-3" id="text-8-1">
<p>
-The weather daemon is run by an unprivileged <code>monkey</code> account (<i>not</i>
-<code>sysadm</code>) which allows <code>monkey</code> on Core shell access. This is also
-executed during the initial phase of configuration, allowing the
-administrator to login on the new weather host as <code>monkey</code> and thus to
-test access to the 1-Wire adapter and devices. To facilitate
-debugging, the <code>sysadm</code> account is included in the <code>monkey</code> group.
+AgentDVR runs as the system user <code>agentdvr</code>, which is created here.
</p>
<div class="org-src-container">
-<a href="roles_t/abbey-weather/tasks/main.yml"><q>roles_t/abbey-weather/tasks/main.yml</q></a><pre class="src src-conf">
-- name: Create monkey.
+<a href="roles_t/abbey-dvr/tasks/main.yml"><q>roles_t/abbey-dvr/tasks/main.yml</q></a><pre class="src src-conf">---
+- name: Create agentdvr.
become: yes
user:
- name: monkey
+ name: agentdvr
system: yes
+ home: /home/agentdvr
+ shell: /bin/bash
+ append: yes
+ groups: video
-- name: Authorize monkey@core.
- become: yes
- vars:
- pubkeyfile: ../Secret/ssh_monkey/id_rsa.pub
- authorized_key:
- user: monkey
- key: <span class="org-string">"{{ lookup('file', pubkeyfile) }}"</span>
- manage_dir: yes
-
-- name: Add {{ ansible_user }} to monkey group.
+- name: Add {{ ansible_user }} to agentdvr group.
become: yes
user:
name: <span class="org-string">"{{ ansible_user }}"</span>
append: yes
- groups: monkey
-</pre>
-</div>
-</div>
-</div>
-<div id="outline-container-org225880e" class="outline-3">
-<h3 id="org225880e"><span class="section-number-3">7.7.</span> Install Weather Daemon</h3>
-<div class="outline-text-3" id="text-7-7">
-<p>
-The weather daemon is kept alive as a Systemd service unit. This task
-creates and starts that service <i>after</i> the host-specific
-<q>files/daemon-HOST</q> file becomes available.
-</p>
-
-<p>
-The <code>ExecStartPre=/bin/sleep 30</code> is intended to avoid recent hangs in
-<code>owread</code>.
-</p>
+ groups: agentdvr
-<div class="org-src-container">
-<a href="roles_t/abbey-weather/tasks/main.yml"><q>roles_t/abbey-weather/tasks/main.yml</q></a><pre class="src src-conf">
-- name: Install weather directory.
+- name: Create /home/agentdvr/.
become: yes
file:
- path: /home/monkey/Weather/Log
+ path: /home/agentdvr
state: directory
- owner: monkey
- group: monkey
- <span class="org-variable-name">mode: u</span>=rwx,g=rx,o=rx
-
-- name: Test for weather daemon script.
- vars:
- dir: ../roles/abbey-weather/files
- file: <span class="org-string">"{{ dir }}/daemon-{{ inventory_hostname }}"</span>
- <span class="org-variable-name">stat: path</span>=<span class="org-string">"{{ file }}"</span>
- delegate_to: localhost
- register: weather
-
-- name: Note missing weather daemon script.
- vars:
- dir: ../roles/abbey-weather/files
- script: <span class="org-string">"{{ dir }}/daemon-{{ inventory_hostname }}"</span>
- debug:
- msg: <span class="org-string">"{{ script }}: not found"</span>
- when: not weather.stat.exists
-
-- name: Install weather daemon.
- become: yes
- vars:
- dir: ../roles/abbey-weather/files
- script: <span class="org-string">"{{ dir }}/daemon-{{ inventory_hostname }}"</span>
- copy:
- src: <span class="org-string">"{{ script }}"</span>
- dest: /home/monkey/Weather/daemon
- owner: monkey
- group: monkey
- <span class="org-variable-name">mode: u</span>=rwx,g=rx,o=
- when: weather.stat.exists
-
-- name: Install weatherd service.
- become: yes
- copy:
- content: |
- [<span class="org-type">Unit</span>]
- <span class="org-variable-name">Description</span>=Weather Logger
- <span class="org-variable-name">After</span>=owserver.service
-
- [<span class="org-type">Service</span>]
- <span class="org-variable-name">User</span>=monkey
- <span class="org-variable-name">ExecStartPre</span>=/bin/sleep 30
- <span class="org-variable-name">ExecStart</span>=/home/monkey/Weather/daemon
- <span class="org-variable-name">Restart</span>=always
-
- [<span class="org-type">Install</span>]
- <span class="org-variable-name">WantedBy</span>=multi-user.target
- dest: /etc/systemd/system/weatherd.service
- when: weather.stat.exists
- notify:
- - Reload Systemd.
- - Restart weather daemon.
-
-- name: Enable/Start weather daemon.
- become: yes
- systemd:
- service: weatherd
- enabled: yes
- state: started
- when: weather.stat.exists
+ owner: agentdvr
+ group: agentdvr
+ <span class="org-variable-name">mode: u</span>=rwx,g=rwxs,o=rx
</pre>
</div>
-
-<div class="org-src-container">
-<a href="roles_t/abbey-weather/handlers/main.yml"><q>roles_t/abbey-weather/handlers/main.yml</q></a><pre class="src src-conf">---
-- name: Reload Systemd.
- become: yes
- command: systemctl daemon-reload
-
-- name: Restart weather daemon.
- become: yes
- systemd:
- service: weatherd
- state: restarted
-</pre>
-</div>
-</div>
</div>
</div>
-<div id="outline-container-org2c65dbc" class="outline-2">
-<h2 id="org2c65dbc"><span class="section-number-2">8.</span> The Abbey DVR Role</h2>
-<div class="outline-text-2" id="text-8">
-<p>
-The abbey uses Zoneminder to record video from PoE IP HD security
-cameras. The Abbey DVR Role installs Zoneminder and configures it to
-record to <q>/Zoneminder/</q>, the mount point for a separate, large
-storage volume. It follows the instructions in Zoneminder's
-<q>README.Debian</q> (in <q>/usr/share/doc/zoneminder/</q>) to create the <code>zm</code>
-database and configure Apache.
-</p>
-</div>
-<div id="outline-container-org4fe0a29" class="outline-3">
-<h3 id="org4fe0a29"><span class="section-number-3">8.1.</span> DVR Machine Setup</h3>
-<div class="outline-text-3" id="text-8-1">
+<div id="outline-container-org9c7e794" class="outline-3">
+<h3 id="org9c7e794"><span class="section-number-3">8.2.</span> Authorize User <code>agentdvr</code></h3>
+<div class="outline-text-3" id="text-8-2">
<p>
-The installation process involves some manual intervention. The first
-time a host is enrolled, Ansible will install the necessary packages,
-but it cannot create the database, nor the database user (yet, in the
-first pass). After adding the new machine to the <code>dvrs</code> group in
-<a href="#orgd0676df">10.2</a>, run Ansible to get the Zoneminder software installed.
+The AgentDVR installer is also run by <code>agentdvr</code>, which is authorized
+to run a handful of system commands. This small set is sufficient
+<i>if</i> the offer to create the system service is declined. In that
+case, the installer will run the program in the terminal.
</p>
-<pre class="example">
-./abbey config HOST
+<div class="org-src-container">
+<a href="roles_t/abbey-dvr/tasks/main.yml"><q>roles_t/abbey-dvr/tasks/main.yml</q></a><pre class="src src-conf">
+- name: Authorize agentdvr.
+ copy:
+ content: |
+ <span class="org-variable-name">ALL ALL</span>=(agentdvr) NOPASSWD: /bin/systemctl,/bin/apt-get,\
+ /sbin/adduser,/sbin/usermod
+ dest: /etc/sudoers.d/agentdvr
</pre>
-
-
-<p>
-Several configuration steps will be skipped because <q>/Zoneminder/</q> has
-not been created yet. To proceed, first create the database and
-database user manually, as described in section <a href="#org6c0f481">Manually Create
-Zoneminder DB and User</a>.
-</p>
</div>
</div>
-<div id="outline-container-org65ae5a4" class="outline-3">
-<h3 id="org65ae5a4"><span class="section-number-3">8.2.</span> Create <q>/Zoneminder/</q></h3>
-<div class="outline-text-3" id="text-8-2">
-<p>
-<q>/Zoneminder/</q> should be a separate, large volume lest Zoneminder fill
-the root file system. For acceptable performance, <q>/Zoneminder/</q>
-should also be the mount point of a solid-state disk (SSD). A
-symbolic link at <q>/var/cache/zoneminder/events</q> targets <q>/Zoneminder</q>
-to make it Zoneminder's "default" storage area. (The <code>PurgeWhenFull</code>
-filter only works with the default storage area in v1.34.)
-</p>
</div>
-</div>
-<div id="outline-container-orgd30766b" class="outline-3">
-<h3 id="orgd30766b"><span class="section-number-3">8.3.</span> Continue Zoneminder Configuration</h3>
+<div id="outline-container-org0f20387" class="outline-3">
+<h3 id="org0f20387"><span class="section-number-3">8.3.</span> Test For <q>AgentDVR/</q></h3>
<div class="outline-text-3" id="text-8-3">
<p>
-Once the <code>zm</code> database (and <code>zmuser</code> database user) are created, and a
-large volume mounted at <q>/Zoneminder/</q>, Ansible can continue with the
-Zoneminder configuration.
-</p>
-
-<pre class="example">
-./abbey configure HOST
-</pre>
-
-
-<p>
-Configuring Zoneminder's cameras is still a manual process as
-described in the final section, <a href="#org1115114">Configure Cameras</a>, below.
-</p>
-</div>
-</div>
-<div id="outline-container-org114a404" class="outline-3">
-<h3 id="org114a404"><span class="section-number-3">8.4.</span> Include Abbey Variables</h3>
-<div class="outline-text-3" id="text-8-4">
-<p>
-Private variables in <q>private/vars-abbey.yml</q> are needed, and included
-here, as in the <code>abbey-core</code> role. The file path is relative to the
-playbook's directory, <q>playbooks/</q>.
+The following task probes for the <q>/home/agentdvr/AgentDVR/</q>
+directory, to detect that the build/install process has completed. It
+registers the results in the <code>agentdvr</code> variable. Several of the
+remaining installation steps are skipped unless
+<code>agentdvr.stat.exists</code>.
</p>
<div class="org-src-container">
-<a href="roles_t/abbey-dvr/tasks/main.yml"><q>roles_t/abbey-dvr/tasks/main.yml</q></a><pre class="src src-conf">---
-- name: Include private abbey variables.
- include_vars: ../private/vars-abbey.yml
+<a href="roles_t/abbey-dvr/tasks/main.yml"><q>roles_t/abbey-dvr/tasks/main.yml</q></a><pre class="src src-conf">
+- name: Test for AgentDVR directory.
+ stat:
+ path: /home/agentdvr/AgentDVR
+ register: agentdvr
+- debug:
+ msg: <span class="org-string">"/home/agentdvr/AgentDVR/ does not yet exist"</span>
+ when: not agentdvr.stat.exists
</pre>
</div>
</div>
</div>
-<div id="outline-container-orgccc0d2c" class="outline-3">
-<h3 id="orgccc0d2c"><span class="section-number-3">8.5.</span> Install Zoneminder v1.34</h3>
-<div class="outline-text-3" id="text-8-5">
-<p>
-The latest version of Zoneminder (1.36) was manually downloaded, built
-and installed, but it immediately had problems, randomly producing
-short events, dropping "problem" cameras entirely, etc. Version 1.34
-did not have those problems, but could still melt down (thrash?) when
-<q>/Zoneminder/</q> was a Seagate Barracuda in a USB3.1gen2 external drive
-enclosure. A Western Digital Passport Ultra seemed to work much
-better, for a short while. Ultimately a solid-state drive (a 2TB
-USB3.2 Gen2 Samsung T7 Shield) mounted at <q>/Zoneminder/</q> got
-Zoneminder 1.34 to work reliably.
-</p>
-
+<div id="outline-container-org6053bf2" class="outline-3">
+<h3 id="org6053bf2"><span class="section-number-3">8.4.</span> Create AgentDVR Service</h3>
+<div class="outline-text-3" id="text-8-4">
<p>
-After uninstalling 1.36, the Debian 11 package (1.34) was installed
-and configured per the instructions in sections "Web server set-up"
-and "Time Zone" in <q>/usr/share/doc/zoneminder/README.Debian.gz</q>.
+This service definition came from the template downloaded (from <a href="https://raw.githubusercontent.com/ispysoftware/agent-install-scripts/main/v2/AgentDVR.service">here</a>)
+by the installer, specifically the <q>linux_setup2.sh</q> script downloaded
+by <q>install.sh</q>.
</p>
<div class="org-src-container">
<a href="roles_t/abbey-dvr/tasks/main.yml"><q>roles_t/abbey-dvr/tasks/main.yml</q></a><pre class="src src-conf">
-- name: Install Zoneminder.
+- name: Install AgentDVR.service.
become: yes
- <span class="org-variable-name">apt: pkg</span>=zoneminder
+ copy:
+ content: |
+ [<span class="org-type">Unit</span>]
+ <span class="org-variable-name">Description</span>=AgentDVR
-- name: Enable Apache modules for Zoneminder.
- become: yes
- apache2_module:
- name: <span class="org-string">"{{ item }}"</span>
- loop: [ cgid, rewrite, expires, headers ]
- notify: Restart Apache2.
+ [<span class="org-type">Service</span>]
+ <span class="org-variable-name">WorkingDirectory</span>=/home/agentdvr/AgentDVR
+ <span class="org-variable-name">ExecStart</span>=/home/agentdvr/AgentDVR/Agent
-- name: Enable Zoneminder Apache configuration.
- become: yes
- command:
- cmd: a2enconf zoneminder
- creates: /etc/apache2/conf-enabled/zoneminder.conf
- notify: Restart Apache2.
+ <span class="org-comment-delimiter"># </span><span class="org-comment">fix memory management issue with dotnet core</span>
+ <span class="org-variable-name">Environment</span>=<span class="org-string">"MALLOC_TRIM_THRESHOLD_=100000"</span>
-- name: Configure MySQL for Zoneminder.
- become: yes
- copy:
- content: |
- [<span class="org-type">mysqld</span>]
- <span class="org-variable-name">sql_mode</span> = NO_ENGINE_SUBSTITUTION
- dest: /etc/mysql/conf.d/zoneminder.cnf
- notify: Restart MySQL.
+ <span class="org-comment-delimiter"># </span><span class="org-comment">to query logs using journalctl, set a logical name here</span>
+ <span class="org-variable-name">SyslogIdentifier</span>=AgentDVR
-- name: Configure PHP date.timezone.
- become: yes
- lineinfile:
- <span class="org-variable-name">regexp: date.timezone ?</span>=
- <span class="org-variable-name">line: date.timezone</span> = {{ lookup(<span class="org-string">'file'</span>, <span class="org-string">'/etc/timezone'</span>) }}
- path: <span class="org-string">"{{ item }}"</span>
- loop:
- - /etc/php/8.2/cli/php.ini
- - /etc/php/8.2/apache2/php.ini
- notify: Restart Apache2.
+ <span class="org-variable-name">User</span>=agentdvr
+
+ <span class="org-comment-delimiter"># </span><span class="org-comment">ensure the service automatically restarts</span>
+ <span class="org-variable-name">Restart</span>=always
+ <span class="org-comment-delimiter"># </span><span class="org-comment">amount of time to wait before restarting the service</span>
+ <span class="org-variable-name">RestartSec</span>=5
-- name: Enable/Start Apache2.
+ [<span class="org-type">Install</span>]
+ <span class="org-variable-name">WantedBy</span>=multi-user.target
+ dest: /etc/systemd/system/AgentDVR.service
+ when: agentdvr.stat.exists
+
+- name: Enable/Start AgentDVR.service.
become: yes
systemd:
- service: apache2
+ service: AgentDVR
enabled: yes
state: started
+ when: agentdvr.stat.exists
</pre>
</div>
-
-<div class="org-src-container">
-<a href="roles_t/abbey-dvr/handlers/main.yml"><q>roles_t/abbey-dvr/handlers/main.yml</q></a><pre class="src src-conf">---
-- name: Restart MySQL.
- become: yes
- systemd:
- service: mysql
- state: restarted
-
-- name: Restart Apache2.
- become: yes
- systemd:
- service: apache2
- state: restarted
-</pre>
</div>
-
+</div>
+<div id="outline-container-orgbdf0cde" class="outline-3">
+<h3 id="orgbdf0cde"><span class="section-number-3">8.5.</span> Create AgentDVR Storage</h3>
+<div class="outline-text-3" id="text-8-5">
<p>
-The following Rsyslog configuration drop-in gets Zoneminder's natter
-out of <q>/var/log/syslog</q>.
+The abbey uses a separate volume to store surveillance recordings,
+lest the DVR program fill the root file system. The volume is mounted
+at <q>/DVR/</q>. The following tasks create <q>/DVR/AgentDVR/video/</q>
+(whether a large volume is mounted there <i>or not</i>!) with appropriate
+permissions so that the instructions for configuring a default storage
+location do not fail.
</p>
<div class="org-src-container">
<a href="roles_t/abbey-dvr/tasks/main.yml"><q>roles_t/abbey-dvr/tasks/main.yml</q></a><pre class="src src-conf">
-- name: Use /var/log/zoneminder.log
+- name: Create /DVR/AgentDVR/.
become: yes
- copy:
- content: |
- :programname,startswith,<span class="org-string">"zm"</span> -/var/log/zoneminder.log
- & stop
- dest: /etc/rsyslog.d/40-zoneminder.conf
-</pre>
-</div>
-</div>
-</div>
-<div id="outline-container-org6940ae7" class="outline-3">
-<h3 id="org6940ae7"><span class="section-number-3">8.6.</span> Create Zoneminder Database</h3>
-<div class="outline-text-3" id="text-8-6">
-<p>
-Zoneminder's MariaDB database is created by the following task, when
-the <code>mysql_db</code> Ansible module supports <code>check_implicit_admin</code>.
-</p>
+ file:
+ state: directory
+ path: /DVR/AgentDVR
+ owner: agentdvr
+ group: agentdvr
+ <span class="org-variable-name">mode: u</span>=rwx,g=rxs,o=
-<div class="org-src-container">
-<pre class="src src-conf">
-- name: Create Zoneminder DB.
+- name: Create /DVR/AgentDVR/video/.
become: yes
- mysql_db:
- check_implicit_admin: yes
- name: zm
- collation: utf8mb4_general_ci
- encoding: utf8mb4
+ file:
+ state: directory
+ path: /DVR/AgentDVR/video
+ owner: agentdvr
+ group: agentdvr
+ <span class="org-variable-name">mode: u</span>=rwx,g=rxs,o=
</pre>
</div>
-
-<p>
-Unfortunately it does not currently, yet the institute prefers the
-more secure Unix socket authentication method. Rather than create a
-privileged DB user, the <code>zm</code> database is created manually (below).
-</p>
</div>
</div>
-<div id="outline-container-org7213248" class="outline-3">
-<h3 id="org7213248"><span class="section-number-3">8.7.</span> Create Zoneminder DB User</h3>
-<div class="outline-text-3" id="text-8-7">
+<div id="outline-container-orgaaba8bb" class="outline-3">
+<h3 id="orgaaba8bb"><span class="section-number-3">8.6.</span> Configure IP Cameras</h3>
+<div class="outline-text-3" id="text-8-6">
<p>
-The following task would create the DB user (<code>mysql_user</code> supports
-<code>check_implicit_admin</code>) <i>but</i> the <code>zm</code> database was not created above.
+A new security camera is setup as described in <a href="#org110d7b3">Cloistering</a>, after
+which the camera should be accessible by name on the abbey networks.
+Assuming <code>ping -c1 new</code> works, the camera's web interface will be
+accessible at <code>http://new/</code>.
</p>
<p>
-The DB user's password is taken from the <code>zoneminder_dbpass</code>
-variable, kept in <q>private/vars-abbey.yml</q>, and generated e.g. with
-the <code>apg -n 1 -x 12 -m 12</code> command.
+The administrator uses this to make the following changes.
</p>
-<div class="org-src-container">
-<a href="private_ex/vars-abbey.yml"><q>private_ex/vars-abbey.yml</q></a><pre class="src src-conf">zoneminder_dbpass: gakJopbikJadsEdd
-</pre>
-</div>
-
-<div class="org-src-container">
-<pre class="src src-conf">
-- name: Create Zoneminder DB user.
- become: yes
- mysql_user:
- check_implicit_admin: yes
- name: zmuser
- password: <span class="org-string">"{{ zoneminder_dbpass }}"</span>
- priv: >-
- zm.*:
- lock tables,alter,create,index,select,insert,update,delete
-</pre>
-</div>
+<ul class="org-ul">
+<li>Set a password on the administrative account.</li>
+<li>Create an unprivileged user with a short password,
+e.g. <code>user:blah</code>.</li>
+<li>Set the frame rate to 5fps. The abbey prefers HD resolution and
+long duration logs, thus fewer frames per second.</li>
+</ul>
</div>
</div>
-<div id="outline-container-org6c0f481" class="outline-3">
-<h3 id="org6c0f481"><span class="section-number-3">8.8.</span> Manually Create Zoneminder DB and User</h3>
-<div class="outline-text-3" id="text-8-8">
+<div id="outline-container-org45c6d54" class="outline-3">
+<h3 id="org45c6d54"><span class="section-number-3">8.7.</span> Configure AgentDVR's Cameras</h3>
+<div class="outline-text-3" id="text-8-7">
<p>
-The Zoneminder database and database user are created manually with
-the following SQL (with the <code>zoneminder_dbpass</code> spliced in). The SQL
-commands are entered at the SQL prompt of the <code>sudo mysql</code> command, or
-perhaps piped into the command.
+After Ansible has configured and started the AgentDVR service, its web
+UI will be available at <code>http://core:8090/</code>. The initial Live View
+will be empty, overlayed with instructions to click the edit button.
</p>
-<div class="org-src-container">
-<pre class="src src-sql"><span class="org-keyword">create</span> database zm
- <span class="org-type">character</span> <span class="org-keyword">set</span> utf8mb4
- <span class="org-keyword">collate</span> utf8mb4_general_ci;
-<span class="org-keyword">grant</span> lock tables,<span class="org-keyword">alter</span>,<span class="org-keyword">create</span>,index,<span class="org-keyword">select</span>,<span class="org-keyword">insert</span>,<span class="org-keyword">update</span>,<span class="org-keyword">delete</span>
- <span class="org-keyword">on</span> zm.*
- <span class="org-keyword">to</span> <span class="org-string">'zmuser'</span>@<span class="org-string">'localhost'</span>
- identified <span class="org-keyword">by</span> <span class="org-string">'{{ zoneminder_dbpass }}'</span>;
-flush <span class="org-keyword">privileges</span>;
-exit;
-</pre>
-</div>
<p>
-Finally, <code>zm</code>'s tables are created, completing the database setup,
+The wizard will ask for each device's general configuration
+parameters. The abbey uses SV3C IP cameras with a full HD stream as
+well as a standard definition "vice stream". AgentDVR wants both.
</p>
-<div class="org-src-container">
-<pre class="src src-sh">sudo mysql < /usr/share/zoneminder/db/zm_create.sql
-</pre>
-</div>
-</div>
-</div>
-<div id="outline-container-orge311581" class="outline-3">
-<h3 id="orge311581"><span class="section-number-3">8.9.</span> Use <q>/Zoneminder/</q></h3>
-<div class="outline-text-3" id="text-8-9">
-<p>
-The following tasks start with a test for the existence of
-<q>/Zoneminder</q>. Configuration tasks that require <q>/Zoneminder/</q> or the
-<code>zm</code> database are executed only when <code>zoneminder.stat.exists</code>. The
-last "Link…" task below "forces" the link, whether the target exists
-or not (yet).
-</p>
-
-<div class="org-src-container">
-<a href="roles_t/abbey-dvr/tasks/main.yml"><q>roles_t/abbey-dvr/tasks/main.yml</q></a><pre class="src src-conf">
-- name: Test for /Zoneminder/.
- stat:
- path: /Zoneminder
- register: zoneminder
-- debug:
- msg: <span class="org-string">"/Zoneminder/ does not yet exist"</span>
- when: not zoneminder.stat.exists
-
-- name: Check /Zoneminder/.
- become: yes
- file:
- state: directory
- path: /Zoneminder
- owner: www-data
- group: www-data
- <span class="org-variable-name">mode: u</span>=rwx,g=rx,o=rx
- when: zoneminder.stat.exists
+<ul class="org-ul">
+<li>General:
+<ul class="org-ul">
+<li>On: yes</li>
+<li>Name: Outside</li>
+<li>Source Type: Network Camera
+<ul class="org-ul">
+<li>Username: user</li>
+<li>Password: blah</li>
+<li>Live URL: rtsp://new.birchwood.private:554/12</li>
+<li>Record URL: rtsp://new.birchwood.private:554/11</li>
+</ul></li>
+</ul></li>
+</ul>
-- name: Link to /Zoneminder/.
- become: yes
- file:
- state: link
- src: /Zoneminder
- path: /var/cache/zoneminder/events
- force: yes
- follow: no
-</pre>
-</div>
-</div>
-</div>
-<div id="outline-container-orgd750b3c" class="outline-3">
-<h3 id="orgd750b3c"><span class="section-number-3">8.10.</span> Configure Zoneminder</h3>
-<div class="outline-text-3" id="text-8-10">
<p>
-The remaining tasks ensure that the <q>/etc/zm/zm.conf</q> file has the
-proper permissions and contains the correct password.
+Additional cameras are added via the "New Device" item in the Server
+Menu. This step is completed when all cameras are streaming to
+AgentDVR's Live View.
</p>
-
-<div class="org-src-container">
-<a href="roles_t/abbey-dvr/tasks/main.yml"><q>roles_t/abbey-dvr/tasks/main.yml</q></a><pre class="src src-conf">
-- name: Set /etc/zm/zm.conf permissions.
- become: yes
- file:
- path: /etc/zm/zm.conf
- owner: root
- group: www-data
- <span class="org-variable-name">mode: u</span>=rw,g=r,o=
-
-- name: Set Zoneminder passphrase.
- become: yes
- lineinfile:
- regexp: <span class="org-string">'^ *ZM_DB_PASS *='</span>
- <span class="org-variable-name">line: ZM_DB_PASS</span>={{ zoneminder_dbpass }}
- path: /etc/zm/zm.conf
-</pre>
</div>
-
+</div>
+<div id="outline-container-orgba96682" class="outline-3">
+<h3 id="orgba96682"><span class="section-number-3">8.8.</span> Configure AgentDVR's Default Storage</h3>
+<div class="outline-text-3" id="text-8-8">
<p>
-Finally, Zoneminder's service unit can be enabled (and started) <i>if</i>
-<q>/Zoneminder/</q> exists. It is assumed that, if <q>/Zoneminder/</q> exists,
-the <code>zm</code> database has also been created, and the service is ready to
-run.
+AgentDVR's web interface is also used to configure a default storage
+location. From the Server Menu (upper left), the administrator chooses
+Configuration Settings, the Storage tab, the Configure button, and the
+add (plus) button. The storage location is set to <q>/DVR/AgentDVR/</q>
+and the "default" toggle is set. Several OK buttons then need to be
+pressed before the task is complete.
</p>
-
-<div class="org-src-container">
-<a href="roles_t/abbey-dvr/tasks/main.yml"><q>roles_t/abbey-dvr/tasks/main.yml</q></a><pre class="src src-conf">
-- name: Enable/Start Zoneminder.
- become: yes
- systemd:
- service: zoneminder
- enabled: yes
- state: started
- when: zoneminder.stat.exists
-</pre>
</div>
</div>
-</div>
-<div id="outline-container-org1115114" class="outline-3">
-<h3 id="org1115114"><span class="section-number-3">8.11.</span> Configure Cameras</h3>
-<div class="outline-text-3" id="text-8-11">
-<p>
-A new security camera is setup as described in <a href="#org110d7b3">Cloistering</a>, after
-which the camera should be accessible by name on the abbey networks.
-Assuming <code>ping -c1 new</code> works, the camera's web interface will be
-accessible at <code>http://new/</code>.
-</p>
-
-<p>
-The abbey's administrator logs into <code>http://new/</code> and turns off any
-OSD (on-screen display). Zoneminder will add its own timestamp, for
-the best accuracy and reliability. The administrator also turns down
-the frame rate to 5fps. The abbey prefers HD resolution (e.g. 1080p)
-and long duration logs, thus fewer frames per second. The
-administrator also creates an unprivileged user with a short password
-e.g. <code>user:gobbledygook</code>.
-</p>
-
+<div id="outline-container-orgcaf1cbe" class="outline-3">
+<h3 id="orgcaf1cbe"><span class="section-number-3">8.9.</span> Configure AgentDVR's Recordings</h3>
+<div class="outline-text-3" id="text-8-9">
<p>
-After Ansible has configured and started Zoneminder, a camera can be
-created by clicking on "Add" in the Zoneminder console. (If the
-Zoneminder host was named "security", the Zoneminder console can be
-found at <code>http://security/zm/</code>.) In the Add dialog, the following
-settings should be changed. (The parenthesized settings are default
-settings that should be checked but are probably already correctly
-set.)
+After a default storage location has been configured, AgentDVR's
+cameras can begin recording. The "Edit Devices" dialog lists (via the
+"Edit Devices" item in the Server Menu) the configured cameras. The
+edit buttons lead to the device settings where the following
+parameters are set (in the Recording and Storage tabs).
</p>
<ul class="org-ul">
-<li>In the "General" tab, specify:
+<li>Recording:
<ul class="org-ul">
-<li>Name: Front</li>
-<li>(Server: None)</li>
-<li>(Source type: Ffmpeg)</li>
-<li>Function: Record</li>
-<li>Enabled: yes</li>
-<li>(Analysis FPS: <blank>)</li>
-<li>(Maximum FPS: <blank>)</li>
-<li>(Alarm Maximum FPS: <blank>)</li>
+<li>Mode: Constant</li>
+<li>Encoder: Raw Record Stream</li>
+<li>Max record time: 900 (15 minutes)</li>
</ul></li>
-<li>In the "Source" tab, specify:
+<li>Storage:
<ul class="org-ul">
-<li>Src path: rtsp://user:gobbledygook@new.small.private.:554/11</li>
-<li>(Method: TCP)</li>
-<li>(Target colorspace: 32 bit colour)</li>
-<li>Capture Resolution: 1920x1080 1080p</li>
-</ul></li>
-<li>In the "Timestamp" tab, specify:
+<li>Location: <i>DVR/AgentDVR</i></li>
+<li>Folder: Outside</li>
+<li>Storage Management:
<ul class="org-ul">
-<li>Timestamp Label X: 10</li>
-<li>Timestamp Label Y: 10</li>
-<li>Font Size: Large</li>
+<li>On: yes</li>
+<li>Max Size: 0 (unlimited)</li>
+<li>Max Age: 168 (7 days)</li>
</ul></li>
-<li>In the "Buffers" tab, specify:
-<ul class="org-ul">
-<li>Image Buffer Size (frames): 40</li>
</ul></li>
</ul>
</div>
</p>
</div>
</div>
-<div id="outline-container-orge3238f6" class="outline-3">
-<h3 id="orge3238f6"><span class="section-number-3">9.3.</span> Include Abbey Variables</h3>
+<div id="outline-container-org3e1f87c" class="outline-3">
+<h3 id="org3e1f87c"><span class="section-number-3">9.3.</span> Include Abbey Variables</h3>
<div class="outline-text-3" id="text-9-3">
<p>
Private variables in <q>private/vars-abbey.yml</q> are needed, as in the
the OTA (over the air) broadcasts.
</p>
-<pre class="example" id="org82b52c7">
+<pre class="example" id="orgd6730f4">
$ tv_grab_zz_sdjson --configure --config-file .mythtv/Mr.Antenna.xml
Cache file for lineups, schedules and programs.
Cache file: [/home/mythtv/.xmltv/tv_grab_zz_sdjson.cache]
kamino:
kessel:
ord-mantell:
- weather:
- hosts:
- anoat:
dvrs:
hosts:
dantooine:
hosts: campus
roles: [ campus, abbey-cloister ]
-- name: Configure Weather
- hosts: weather
- roles: [ abbey-weather ]
-
- name: Configure DVRs
hosts: dvrs
roles: [ abbey-dvr ]
<p>
The abbey changes location almost weekly, so its timezone changes
occasionally. Droplet does not move. Gate and other simple servers
-(the weather monitors) are kept in UTC. Core, the DVRs, TVRs, and the
-desktops all want updating to the current local timezone. The
-desktops are managed maually, but the rest can all be updated using
-Ansible.
+are kept in UTC. Core, the DVRs, TVRs, Home Assistant and the
+desktops all want updating to the current local timezone. Home
+Assistant and the desktops are managed maually, but the rest can all
+be updated using Ansible.
</p>
<p>
- hosts: dvrs
tasks:
- - name: Restart Zoneminder.
+ - name: Restart AgentDVR.
become: yes
systemd:
- service: <span class="org-string">"{{ item }}"</span>
+ service: AgentDVR
state: restarted
- loop: [ mysql, zoneminder ]
when: new_tz.changed
- hosts: tvrs
</div>
<div id="postamble" class="status">
<p class="author">Author: Matt Birkholz</p>
-<p class="date">Created: 2024-09-03 Tue 08:46</p>
+<p class="date">Created: 2024-09-20 Fri 13:28</p>
<p class="validation"><a href="https://validator.w3.org/check?uri=referer">Validate</a></p>
</div>
</body>