summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FS/FS/Schema.pm5
-rw-r--r--FS/FS/part_export/saisei.pm190
-rw-r--r--FS/FS/tower.pm10
-rw-r--r--FS/FS/tower_sector.pm14
-rw-r--r--httemplate/edit/process/tower.html2
-rw-r--r--httemplate/edit/tower.html7
-rw-r--r--httemplate/elements/tr-tower_sectors.html310
7 files changed, 488 insertions, 50 deletions
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 91c91f8..cc74703 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -4891,6 +4891,8 @@ sub tables_hashref {
'height', 'decimal', 'NULL', '', '', '',
'veg_height', 'decimal', 'NULL', '', '', '',
'color', 'varchar', 'NULL', 6, '', '',
+ 'up_rate', 'int', 'NULL', '', '', '',
+ 'down_rate', 'int', 'NULL', '', '', '',
],
'primary_key' => 'towernum',
'unique' => [ [ 'towername' ] ], # , 'agentnum' ] ],
@@ -4916,6 +4918,9 @@ sub tables_hashref {
'east', 'decimal', 'NULL', '10,7', '', '',
'south', 'decimal', 'NULL', '10,7', '', '',
'north', 'decimal', 'NULL', '10,7', '', '',
+ 'title', 'varchar', 'NULL', $char_d,'', '',
+ 'up_rate', 'int', 'NULL', '', '', '',
+ 'down_rate', 'int', 'NULL', '', '', '',
],
'primary_key' => 'sectornum',
'unique' => [ [ 'towernum', 'sectorname' ], [ 'ip_addr' ], ],
diff --git a/FS/FS/part_export/saisei.pm b/FS/FS/part_export/saisei.pm
index fc0dee5..f76051e 100644
--- a/FS/FS/part_export/saisei.pm
+++ b/FS/FS/part_export/saisei.pm
@@ -24,18 +24,29 @@ Saisei integration for Freeside
This export offers basic svc_broadband provisioning for Saisei.
-This is a customer integration with Saisei. This will setup a rate plan and tie
-the rate plan to a host via the Saisei API when the broadband service is provisioned.
-It will also untie the rate plan via the API upon unprovisioning of the broadband service.
+This is a customer integration with Saisei. This will setup a rate plan and tie
+the rate plan to a host and access point via the Saisei API when the broadband service is provisioned.
+It will also untie the rate plan via the API upon unprovisioning of the broadband service.
-This export will use the broadband service descriptive label for the Saisei rate plan name and
-will use the email from the first contact for the Saisei username that will be
-attached to this rate plan. It will use the Saisei default Access Point.
+Add a new export and fill out required fields:
+<UL>
+<LI>Hostname or IP - <I>Host name to Saisei API</I></LI>
+<LI>Port - <I>Port number to Saisei API</I></LI>
+<LI>User Name - <I>Saisei API user name</I></LI>
+<LI>Password - <I>Saisei API password</I></LI>
+</UL>
+Create a broadband service. The broadband service name will become the Saisei rate plan name.
+Set the upload and download speed, and set the modifier to fixed.
+Set IP Address to required.
+Attach Saisei export to service
+
+Create a tower and add a sector to that tower. The sector name will be the name of the access point,
+Make sure you have set an up and down rate for the Tower and Sector.
+
+When you provision the service, enter the ip address associated to this service.
+Select the Tower and Sector for it's access point.
-Hostname or IP - Host name to Saisei API
-Port - <I>Port number to Saisei API
-User Name - <I>Saisei API user name
-Password - <I>Saisei API password
+When the service is provisioned it will auto setup the rate plan.
This module also provides generic methods for working through the L</Saisei API>.
@@ -58,27 +69,37 @@ tie my %options, 'Tie::IxHash',
'options' => \%options,
'notes' => <<'END',
This is a customer integration with Saisei. This will setup a rate plan and tie
-the rate plan to a host via the Saisei API when the broadband service is provisioned.
-It will also untie the rate plan via the API upon unprovisioning of the broadband service.
-<P>This export will use the broadband service descriptive label for the Saisei rate plan name and
-will use the email from the first contact for the Saisei username that will be
-attached to this rate plan. It will use the Saisei default Access Point.
+the rate plan to a host and access point via the Saisei API when the broadband service is provisioned.
+It will also untie the rate plan via the API upon unprovisioning of the broadband service.
<P>
-Required Fields:
+Add a new export and fill out required fields:
<UL>
<LI>Hostname or IP - <I>Host name to Saisei API</I></LI>
<LI>Port - <I>Port number to Saisei API</I></LI>
<LI>User Name - <I>Saisei API user name</I></LI>
<LI>Password - <I>Saisei API password</I></LI>
</UL>
+Create a broadband service. The broadband service name will become the Saisei rate plan name.
+Set the upload and download speed, and set the modifier to fixed.
+Set IP Address to required.
+Attach Saisei export to service
+<P>
+Create a tower and add a sector to that tower. The sector name will be the name of the access point,
+Make sure you have set an up and down rate for the Tower and Sector.
+<P>
+When you provision the service, enter the ip address associated to this service.
+Select the Tower and Sector for it's access point.
+<P>
+When the service is provisioned it will auto setup the rate plan.
END
);
sub _export_insert {
my ($self, $svc_broadband) = @_;
- my $rateplan_name = $svc_broadband->{Hash}->{description};
- $rateplan_name =~ s/\s/_/g;
+ my $service_part = FS::Record::qsearchs( 'part_svc', { 'svcpart' => $svc_broadband->{Hash}->{svcpart} } );
+ my $rateplan_name = $service_part->{Hash}->{svc};
+ $rateplan_name =~ s/\s/_/g;
# load needed info from our end
my $cust_main = $svc_broadband->cust_main;
@@ -99,19 +120,13 @@ sub _export_insert {
# set rateplan to existing one or newly created one.
my $rateplan = $existing_rateplan ? $existing_rateplan : $self->api_get_rateplan($rateplan_name);
- my @email = map { $_->emailaddress } FS::Record::qsearch({
- 'table' => 'cust_contact',
- 'select' => 'emailaddress',
- 'addl_from' => ' JOIN contact_email USING (contactnum)',
- 'hashref' => { 'custnum' => $cust_main->{Hash}->{custnum}, },
- });
- my $username = $email[0];
- my $description = $cust_main->{Hash}->{first}." ".$cust_main->{Hash}->{last};
+ my $username = $svc_broadband->{Hash}->{svcnum};
+ my $description = $svc_broadband->{Hash}->{description};
if (!$username) {
$self->{'__saisei_error'} = 'no username - can not export';
- warn "No email found $username\n" if $self->option('debug');
- return;
+ warn "No user $username\n" if $self->option('debug');
+ return $self->api_error;
}
else {
# check for existing user.
@@ -125,12 +140,65 @@ sub _export_insert {
my $user = $existing_user ? $existing_user : $self->api_get_user($username);
## add access point ?
-
- ## tie host to user
- $self->api_add_host_to_user($user->{collection}->[0]->{name}, $rateplan->{collection}->[0]->{name}, $svc_broadband->{Hash}->{ip_addr}) unless $self->{'__saisei_error'};
+ my $tower_sector = FS::Record::qsearchs({
+ 'table' => 'tower_sector',
+ 'select' => 'tower.towername,
+ tower.up_rate as toweruprate,
+ tower.down_rate as towerdownrate,
+ tower_sector.sectorname,
+ tower_sector.up_rate as sectoruprate,
+ tower_sector.down_rate as sectordownrate ',
+ 'addl_from' => 'LEFT JOIN tower USING ( towernum )',
+ 'hashref' => {
+ 'sectornum' => $svc_broadband->{Hash}->{sectornum},
+ },
+ });
+
+ my $existing_tower_ap;
+ my $tower_name = $tower_sector->{Hash}->{towername};
+ $tower_name =~ s/\s/_/g;
+
+ #check if tower has been set up as an access point.
+ $existing_tower_ap = $self->api_get_accesspoint($tower_name) unless $self->{'__saisei_error'};;
+
+ #if tower does not exist as an access point create it.
+ $self->api_create_accesspoint(
+ $tower_name,
+ $tower_sector->{Hash}->{toweruprate},
+ $tower_sector->{Hash}->{towerdownrate}
+ ) unless $existing_tower_ap;
+
+ my $existing_sector_ap;
+ my $sector_name = $tower_sector->{Hash}->{sectorname};
+ $sector_name =~ s/\s/_/g;
+
+ #check if sector has been set up as an access point.
+ $existing_sector_ap = $self->api_get_accesspoint($sector_name);
+
+ #if sector does not exist as an access point create it.
+ $self->api_create_accesspoint(
+ $sector_name,
+ $tower_sector->{Hash}->{sectoruprate},
+ $tower_sector->{Hash}->{sectordownrate},
+ $tower_name,
+ ) unless $existing_sector_ap;
+
+ # Attach newly created sector to it's tower.
+ $self->api_modify_accesspoint($sector_name, $tower_name) unless ($self->{'__saisei_error'} || $existing_sector_ap);
+
+ # set access point to existing one or newly created one.
+ my $accesspoint = $existing_sector_ap ? $existing_sector_ap : $self->api_get_accesspoint($sector_name);
+
+ ## tie host to user add sector name as access point.
+ $self->api_add_host_to_user(
+ $user->{collection}->[0]->{name},
+ $rateplan->{collection}->[0]->{name},
+ $svc_broadband->{Hash}->{ip_addr},
+ $accesspoint->{collection}->[0]->{name},
+ ) unless $self->{'__saisei_error'};
}
- return '';
+ return $self->api_error;
}
@@ -229,7 +297,7 @@ sub api_call {
=head2 api_error
-Returns the error string set by L</PortaOne API> methods,
+Returns the error string set by L</Saisei API> methods,
or a blank string if most recent call produced no errors.
=cut
@@ -300,14 +368,14 @@ Gets user info for specific access point.
sub api_get_accesspoint {
my $self = shift;
- my $accesspoint;
+ my $accesspoint = shift;
my $get_accesspoint = $self->api_call("GET", "/access_points/$accesspoint");
return if $self->api_error;
- $self->{'__saisei_error'} = "Did not receive any user info"
+ $self->{'__saisei_error'} = "Did not receive any access point info"
unless $get_accesspoint;
- return;
+ return $get_accesspoint;
}
=head2 api_create_rateplan
@@ -397,19 +465,44 @@ Creates a access point.
=cut
sub api_create_accesspoint {
- my ($self,$accesspoint) = @_;
+ my ($self,$accesspoint, $uprate, $downrate) = @_;
# this has not been tested, but should work, if needed.
- #my $new_accesspoint = $self->api_call(
- # "PUT",
- # "/access_points/$accesspoint",
- # {
- # 'description' => 'my description',
- # },
- #);
-
- #$self->{'__saisei_error'} = "Access point not created"
- # unless $new_accesspoint; # should never happen
+ my $new_accesspoint = $self->api_call(
+ "PUT",
+ "/access_points/$accesspoint",
+ {
+ 'downstream_rate_limit' => $downrate,
+ 'upstream_rate_limit' => $uprate,
+ },
+ );
+
+ $self->{'__saisei_error'} = "Access point not created"
+ unless $new_accesspoint; # should never happen
+ return;
+
+}
+
+=head2 api_modify_accesspoint
+
+Modify a access point.
+
+=cut
+
+sub api_modify_accesspoint {
+ my ($self, $accesspoint, $uplink) = @_;
+
+ my $modified_rateplan = $self->api_call(
+ "PUT",
+ "/access_points/$accesspoint",
+ {
+ 'uplink' => $uplink, # name of attached access point
+ },
+ );
+
+ $self->{'__saisei_error'} = "Rate Plan not modified"
+ unless $modified_rateplan; # should never happen
+
return;
}
@@ -421,7 +514,7 @@ ties host to user, rateplan and default access point.
=cut
sub api_add_host_to_user {
- my ($self,$user, $rateplan, $ip) = @_;
+ my ($self,$user, $rateplan, $ip, $accesspoint) = @_;
my $new_host = $self->api_call(
"PUT",
@@ -429,6 +522,7 @@ sub api_add_host_to_user {
{
'user' => $user,
'rate_plan' => $rateplan,
+ 'access_point' => $accesspoint,
},
);
diff --git a/FS/FS/tower.pm b/FS/FS/tower.pm
index f371ec9..5dcf1f8 100644
--- a/FS/FS/tower.pm
+++ b/FS/FS/tower.pm
@@ -44,6 +44,14 @@ Tower name
Disabled flag, empty or 'Y'
+=item up_rate
+
+Up Rate for towner
+
+=item down_rate
+
+Down Rate for tower
+
=back
=head1 METHODS
@@ -97,6 +105,8 @@ sub check {
|| $self->ut_floatn('height')
|| $self->ut_floatn('veg_height')
|| $self->ut_alphan('color')
+ || $self->ut_numbern('up_rate')
+ || $self->ut_numbern('down_rate')
;
return $error if $error;
diff --git a/FS/FS/tower_sector.pm b/FS/FS/tower_sector.pm
index 3fadc86..b58cacf 100644
--- a/FS/FS/tower_sector.pm
+++ b/FS/FS/tower_sector.pm
@@ -88,6 +88,18 @@ The coverage map, as a PNG.
The coordinate boundaries of the coverage map.
+=item title
+
+The sector title.
+
+=item up_rate
+
+Up rate for sector.
+
+=item down_rate
+
+down rate for sector.
+
=back
=head1 METHODS
@@ -150,6 +162,8 @@ sub check {
|| $self->ut_numbern('downtilt')
|| $self->ut_floatn('sector_range')
|| $self->ut_numbern('margin')
+ || $self->ut_numbern('up_rate')
+ || $self->ut_numbern('down_rate')
|| $self->ut_anything('image')
|| $self->ut_sfloatn('west')
|| $self->ut_sfloatn('east')
diff --git a/httemplate/edit/process/tower.html b/httemplate/edit/process/tower.html
index d14ac56..e17cd55 100644
--- a/httemplate/edit/process/tower.html
+++ b/httemplate/edit/process/tower.html
@@ -5,7 +5,7 @@
'fields' => [qw(
sectorname ip_addr height freq_mhz direction width
downtilt v_width margin
- sector_range
+ sector_range up_rate down_rate
)],
},
&>
diff --git a/httemplate/edit/tower.html b/httemplate/edit/tower.html
index 377a33e..6607888 100644
--- a/httemplate/edit/tower.html
+++ b/httemplate/edit/tower.html
@@ -12,6 +12,8 @@
'altitude',
'height',
'veg_height',
+ 'up_rate',
+ 'down_rate',
{ field => 'sectornum',
type => 'tower_sector',
o2m_table => 'tower_sector',
@@ -30,6 +32,8 @@
'height' => 'Height (feet)',
'veg_height' => 'Vegetation height (feet)',
'color' => 'Color',
+ 'up_rate' => 'Up Rate (Kbps)',
+ 'down_rate' => 'Down Rate (Kbps)',
},
&>
<%init>
@@ -38,7 +42,8 @@ my $m2_error_callback = sub { # reconstruct the list
my ($cgi, $object) = @_;
my @fields = qw(
- sectorname ip_addr height freq_mhz direction width tilt v_width margin sector_range
+ sectorname ip_addr height freq_mhz direction width tilt v_width margin
+ sector_range up_rate down_rate
);
map {
diff --git a/httemplate/elements/tr-tower_sectors.html b/httemplate/elements/tr-tower_sectors.html
new file mode 100644
index 0000000..6843f4f
--- /dev/null
+++ b/httemplate/elements/tr-tower_sectors.html
@@ -0,0 +1,310 @@
+<%shared>
+# kind of a hack...
+my ($export) = FS::tower_sector->part_export;
+my $antenna_types; # will be an ordered hash
+if ($export and $export->can('get_antenna_types')) {
+ $antenna_types = $export->get_antenna_types;
+}
+</%shared>
+<%init>
+my %opt = @_;
+my $tower = $opt{'object'};
+my $towernum = $tower->towernum;
+my $cgi = $opt{'cgi'};
+
+my $tabcounter = 0;
+
+my @fields = qw(
+ sectorname ip_addr height freq_mhz direction width downtilt v_width
+ db_high db_low sector_range
+ power line_loss antenna_gain hardware_typenum up_rate down_rate
+);
+
+my @sectors;
+if ( $cgi->param('error') ) {
+ foreach my $k ($cgi->param) {
+ if ($k =~ /^sectornum\d+$/) {
+ my $sectornum = $cgi->param($k);
+ my $sector = FS::tower_sector->new({
+ 'sectornum' => $sectornum,
+ 'towernum' => $towernum,
+ map { $_ => scalar($cgi->param($k.'_'.$_)) } @fields,
+ });
+ push @sectors, $sector if length($sector->sectorname);
+ }
+ }
+} elsif ( $towernum ) {
+ @sectors = $tower->tower_sector;
+} # else new mode, no sectors yet
+
+my $id = $opt{id} || $opt{field} || 'sectornum';
+
+</%init>
+<& tablebreak-tr-title.html, value => 'Sectors' &>
+
+<style>
+ .ui-tabs-nav a {
+ padding: 6px 9px;
+ font-weight: bold;
+ }
+ .ui-tabs-nav li {
+ border-top-left-radius: 0.5em;
+ border-top-right-radius: 0.5em;
+ }
+ .ui-tabs-active li {
+ border-bottom-color: #fff;
+ }
+ .ui-tabs {
+ font-weight: bold;
+ }
+ .ui-tabs label {
+ padding-top: 3px;
+ width: 140px;
+ display: inline-block;
+ text-align: right;
+ }
+ .ui-tabs input, .ui-spinner {
+ border: 1px solid #666;
+ border-radius: 2px;
+ font-size: 13.3px;
+ text-align: right;
+ font-weight: normal;
+ padding: 1px;
+ }
+ .ui-tabs input { /* but not spinner, messes it up */
+ margin-left: 1px;
+ margin-right: 1px;
+ }
+ .ui-tabs input:focus {
+ border-color: #7e0079;
+ background-color: #ffffdd;
+ }
+ .ui-spinner input { /* use the spinner's border and padding */
+ border: none;
+ text-align: left;
+ }
+ .ui-tabs p {
+ margin-top: 8px;
+ margin-bottom: 8px;
+ }
+
+</style>
+
+
+<tr>
+ <td colspan=2>
+%# prototypes
+ <div style="display: none">
+<& .tab, id => $id . '_P' &>
+<& .panel, id => $id . '_P' &>
+ </div>
+
+%# main container
+ <div id="<% $id %>_tabs">
+ <ul>
+% foreach my $sector (@sectors) {
+<& .tab, sector => $sector, id => $id . $tabcounter &>
+% $tabcounter++;
+% }
+ </ul>
+
+% $tabcounter = 0;
+% foreach my $sector (@sectors) {
+<& .panel, sector => $sector, id => $id . $tabcounter &>
+% $tabcounter++;
+% }
+ </div>
+ </td>
+</tr>
+<script>
+$(function() {
+ var tabcounter = <% $tabcounter %>;
+ var id = <% $id |js_string %>;
+ //create tab bar
+ var tabs = $( '#'+id+'_tabs' ).tabs();
+
+ function changedSectorName() {
+ var this_panel = $(this).closest('div');
+ var this_tab = tabs.find('#' + this_panel.prop('id') + '_tab');
+ // if this is the last panel, make a new one
+ if (this_panel.next().length == 0) {
+ addSector();
+ }
+ // and update the current tab's text with the sector name
+ this_tab.find('a').text($(this).val());
+ }
+
+ var tab_proto = $('#'+id+'_P_tab');
+ var panel_proto = $('#'+id+'_P');
+
+ function addSector() {
+ var new_tab = tab_proto.clone();
+ var new_panel = panel_proto.clone();
+ // replace proto placeholder with the counter value, in all id and
+ // name properties in new_panel and its children
+ new_panel.add( new_panel.find('*') ).each(function() {
+ this.id = this.id.replace('_P', tabcounter);
+ if (this.name) {
+ this.name = this.name.replace('_P', tabcounter);
+ }
+ });
+ tabcounter++;
+ // and set the handler up on it
+ new_panel.find('.input-sectorname').on('change', changedSectorName);
+
+ // also update the tab itself
+ new_tab.find('a').prop('href', '#' + new_panel.prop('id'));
+ new_tab.prop('id', new_panel.prop('id') + '_tab');
+
+ tabs.append(new_panel);
+ tabs.children('ul:first').append(new_tab);
+
+ tabs.tabs('refresh');
+ }
+
+ $('.dbspinner').spinner({ step: 5 });
+
+ $('.input-sectorname').on('change', changedSectorName);
+ addSector();
+
+});
+</script>
+<%def .tab>
+% my %opt = @_;
+% my $sector = $opt{sector};
+% my $id = $opt{id};
+% my $title = $sector ? $sector->sectorname : mt('Add new');
+ <li id="<% $id %>_tab">
+ <a href="#<% $id %>"><% $title |h %></a>
+ </li>
+</%def>
+<%def .panel>
+% my %opt = @_;
+% my $sector = $opt{sector} || FS::tower_sector->new({});
+% my $id = $opt{id}; # sectornumX
+<div id="<% $id %>">
+% # no id on this one, the panel gets the "sectornumX" id
+ <input type="hidden" name="<% $id %>" value="<% $sector->sectornum |h %>">
+ <p>
+ <label><% emt('Sector name') %></label>
+ <input style="text-align: left"
+ class="input-sectorname"
+ id="<% $id %>_sectorname"
+ name="<% $id %>_sectorname"
+ value="<% $sector->sectorname |h %>">
+
+ <label><% emt('IP address') %></label>
+ <input style="text-align: left"
+ id="<% $id %>_ip_addr"
+ name="<% $id %>_ip_addr"
+ value="<% $sector->ip_addr |h %>">
+ </p>
+ <p>
+ <label for="<% $id %>_height"><% emt('Antenna height') %></label>
+ <input size="3"
+ id="<% $id %>_height"
+ name="<% $id %>_height"
+ value="<% $sector->height |h %>">
+ <% emt('feet above ground') %>
+ </p>
+ <p>
+ <label for="<% $id %>_direction"><% emt('Azimuth') %></label>
+ <input size="3"
+ id="<% $id %>_direction"
+ name="<% $id %>_direction"
+ value="<% $sector->direction |h %>">&deg;
+ <label for="<% $id %>_downtilt"><% emt('Down tilt') %></label>
+ <input size="2"
+ id="<% $id %>_downtilt"
+ name="<% $id %>_downtilt"
+ value="<% $sector->downtilt |h %>">&deg;
+ </p>
+
+ <p>
+ <label for="<% $id %>_freq_mhz"><% emt('Frequency') %></label>
+ <input size="4"
+ id="<% $id %>_freq_mhz"
+ name="<% $id %>_freq_mhz"
+ value="<% $sector->freq_mhz |h %>">
+ <% emt('MHz') %>
+ </p>
+
+ <p>
+ <label for="<% $id %>_power"><% emt('Transmit power') %></label>
+ <input size="3"
+ id="<% $id %>_power"
+ name="<% $id %>_power"
+ value="<% $sector->power |h %>">
+ <% emt('dBm') %><br>
+ <label for="<% $id %>_antenna_gain">+ </label>
+ <input size="3"
+ id="<% $id %>_antenna_gain"
+ name="<% $id %>_antenna_gain"
+ value="<% $sector->antenna_gain |h %>">
+ <% emt('dB antenna gain') %><br>
+ <label for="<% $id %>_line_loss">&ndash; </label>
+ <input size="3"
+ id="<% $id %>_line_loss"
+ name="<% $id %>_line_loss"
+ value="<% $sector->line_loss |h %>">
+ <% emt('dB line loss') %>
+
+% if ( $antenna_types ) {
+ <p>
+ <label for="<% $id %>_hardware_typenum"><% emt('Antenna type') %></label>
+ <& /elements/select.html,
+ field => $id.'_hardware_typenum',
+ options => [ '', keys %$antenna_types ],
+ labels => $antenna_types,
+ curr_value => $sector->hardware_typenum,
+ &>
+ </p>
+% }
+% # this next section might not be necessary if you enter an antenna type
+ <p>
+ <label for="<% $id %>_width"><% emt('Horizontal beam') %></label>
+ <input size="3"
+ id="<% $id %>_width"
+ name="<% $id %>_width"
+ value="<% $sector->width |h %>">&deg;
+ <label for="<% $id %>_v_width"><% emt('Vertical beam') %></label>
+ <input size="2"
+ id="<% $id %>_v_width"
+ name="<% $id %>_v_width"
+ value="<% $sector->v_width |h %>">&deg;
+ </p>
+
+ <label><% emt('Signal margin') %></label>
+ <div style="display: inline-block; vertical-align: top">
+ <input class="dbspinner"
+ size="4"
+ id="<% $id %>_db_high"
+ name="<% $id %>_db_high"
+ value="<% $sector->db_high |h %>">
+ <% emt('dB (high quality)') %>
+ <br>
+
+ <input class="dbspinner"
+ size="4"
+ id="<% $id %>_db_low"
+ name="<% $id %>_db_low"
+ value="<% $sector->db_low |h %>">
+ <% emt('dB (low quality)') %>
+ </div>
+ <p>
+ <label><% emt('Up Rate (Kbps)') %></label>
+ <input style="text-align: left"
+ id="<% $id %>_up_rate"
+ name="<% $id %>_up_rate"
+ value="<% $sector->up_rate |h %>">
+ </p>
+ <p>
+ <label><% emt('Down Rate (Kbps)') %></label>
+ <input style="text-align: left"
+ id="<% $id %>_down_rate"
+ name="<% $id %>_down_rate"
+ value="<% $sector->down_rate |h %>">
+ </p>
+
+</div>
+</%def>