Thermostat
Three rooms with their own settings over factory defaults that they read live, heated minute by minute through a day with a band around the target, hour by hour, and the band's trade-off between switching the heater and holding the temperature.
Every screen below was recorded under CPython. When this page was built, the EML interpreter replayed each session from the same input and printed the same bytes.
About
Three rooms heated through a day. Each room has its own settings over the factory defaults, and a band around the target keeps the heater from switching every minute.
main.eml- the menu, the rooms table, setting a room or the factory, a day, one room hour by hour, and other bandsheat.eml- the factory defaults, the rooms, the target for a minute, and the day minute by minute
How each part works:
- The factory defaults are on a class: a day target of 20.0 degrees, a night target of 17.0, a band of 0.5, and the day from 06:00 to 22:00. A room keeps only its own settings. As in the corpus case
thermostat-class-defaults: - reading a setting falls back to the factory at the moment of asking; - changing the factory moves at once every room without its own value; - setting one room's value moves no other room, and not the factory. - Temperatures are whole tenths of a degree, and time runs in minutes. A heating room warms by 0.2 degrees a minute, and a room warmer than outside (5.0) cools by 0.1. The heater comes on at target - band and goes off at target + band.
- The band's trade-off: within the day, from the moment the room first comes within the band, the program records the lowest and highest temperature. A band of 0 switches the heater on at least 9 times as often as a band of 1.0, and holds the temperature closer. Bands less than 0.3 apart can come out the other way by a little, since the heater warms in steps of 0.2.
What is checked: menu choices 1 to 7; one of the three rooms; one of the five settings; a temperature from 5.0 to 30.0, a band from 0 to 3.0, a time as hh:mm; a day that starts before it ends, for every room a factory change would move. An empty answer cancels; factory as a value makes a room follow the factory again.
Sessions: sessions/basic.in gives: - the rooms (the bedroom with its own night, the nursery with its own day and band) and a day; - the living room hour by hour, and with bands 0, 0.2, 0.5 and 1.0: 463 switches down to 46; - the factory day lowered to 19.0, which the living room and the bedroom follow and the nursery does not; - the living room's own night of 15.5, the nursery back to the factory day, and the day again.
sessions/bad-input.in gives: - menu choices 0 and x; - settings: a cancel, a kitchen, a cancel, an unknown setting, a cancel, 35, 21.55, a band of 4, the times 6:30 and 24:00, and a morning after the evening; - the factory: a cancel, a cancel, abc, and an evening before the morning; - a cancel for hour by hour, and a kitchen for the bands.
Built on the verified corpus case thermostat-class-defaults (a default on the class, an override on the instance, and a live fallback between them).
Recorded sessions
What the screen shows while someone uses the program. Each typed line appears after its prompt, the way a terminal shows it.
bad-input
interpreter: byte-equal== Thermostat ==
Three rooms over the factory settings, each with its own changes, heated through a day.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 0
Pick a number from 1 to 7.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> x
Pick a number from 1 to 7.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 2
room (living, bedroom or nursery)>
Cancelled.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 2
room (living, bedroom or nursery)> kitchen
Type living, bedroom or nursery.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 2
room (living, bedroom or nursery)> living
setting (day, night, band, morning or evening)>
Cancelled.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 2
room (living, bedroom or nursery)> living
setting (day, night, band, morning or evening)> colour
Type day, night, band, morning or evening.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 2
room (living, bedroom or nursery)> living
setting (day, night, band, morning or evening)> day
value, or factory to follow the factory again>
Cancelled.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 2
room (living, bedroom or nursery)> living
setting (day, night, band, morning or evening)> day
value, or factory to follow the factory again> 35
Type a temperature from 5.0 to 30.0 degrees.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 2
room (living, bedroom or nursery)> living
setting (day, night, band, morning or evening)> day
value, or factory to follow the factory again> 21.55
Type a temperature from 5.0 to 30.0 degrees.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 2
room (living, bedroom or nursery)> living
setting (day, night, band, morning or evening)> band
value, or factory to follow the factory again> 4
Type a band from 0 to 3.0 degrees.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 2
room (living, bedroom or nursery)> living
setting (day, night, band, morning or evening)> morning
value, or factory to follow the factory again> 6:30
Type a time such as 06:30.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 2
room (living, bedroom or nursery)> living
setting (day, night, band, morning or evening)> morning
value, or factory to follow the factory again> 24:00
Type a time such as 06:30.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 2
room (living, bedroom or nursery)> living
setting (day, night, band, morning or evening)> morning
value, or factory to follow the factory again> 22:30
The day has to start before it ends.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 3
setting (day, night, band, morning or evening)>
Cancelled.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 3
setting (day, night, band, morning or evening)> day
factory value>
Cancelled.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 3
setting (day, night, band, morning or evening)> day
factory value> abc
Type a temperature from 5.0 to 30.0 degrees.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 3
setting (day, night, band, morning or evening)> evening
factory value> 05:00
The day has to start before it ends, in living too.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 5
room (living, bedroom or nursery)>
Cancelled.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 6
room (living, bedroom or nursery)> kitchen
Type living, bedroom or nursery.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 7
Bye.
What was typed (56 lines)
0
x
2
2
kitchen
2
living
2
living
colour
2
living
day
2
living
day
35
2
living
day
21.55
2
living
band
4
2
living
morning
6:30
2
living
morning
24:00
2
living
morning
22:30
3
3
day
3
day
abc
3
evening
05:00
5
6
kitchen
7
basic
interpreter: byte-equal== Thermostat ==
Three rooms over the factory settings, each with its own changes, heated through a day.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 1
room day night band morning evening
living 20.0 factory 17.0 factory 0.5 factory 06:00 factory 22:00 factory
bedroom 20.0 factory 16.0 own 0.5 factory 06:00 factory 22:00 factory
nursery 22.0 own 17.0 factory 0.3 own 06:00 factory 22:00 factory
Factory: day 20.0, night 17.0, band 0.5, day from 06:00 to 22:00.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 4
A day from 00:00, each room starting at 18.0 degrees with 5.0 outside:
room heating switched on lowest highest
living 7h 55m 92 16.5 20.5
bedroom 7h 55m 91 15.5 20.5
nursery 7h 57m 152 16.7 22.3
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 5
room (living, bedroom or nursery)> living
living, hour by hour (the temperature on the hour, minutes heating in the hour):
00:00 18.0 15 01:00 16.5 20 02:00 16.5 20 03:00 16.5 20
04:00 16.5 20 05:00 16.5 20 06:00 16.5 30 07:00 19.5 20
08:00 19.5 20 09:00 19.5 20 10:00 19.5 20 11:00 19.5 20
12:00 19.5 20 13:00 19.5 20 14:00 19.5 20 15:00 19.5 20
16:00 19.5 20 17:00 19.5 20 18:00 19.5 20 19:00 19.5 20
20:00 19.5 20 21:00 19.5 20 22:00 19.5 10 23:00 16.5 20
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 6
room (living, bedroom or nursery)> living
living over a day with other bands:
band switched on heating lowest highest
0.0 463 7h 57m 20.0 20.2
0.2 231 7h 57m 19.8 20.2
0.5 92 7h 55m 19.5 20.5
1.0 46 7h 55m 19.0 21.0
The lowest and highest are in the day, from the moment the room first comes within the band. A narrow band holds the temperature close to the target and switches the heater far more often; a wide one switches less and lets it swing. Bands less than 0.3 degrees apart can come out the other way by a little, since the heater warms in steps of 0.2 degrees.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 3
setting (day, night, band, morning or evening)> day
factory value> 19.0
Factory day is now 19.0.
following it at once: living, bedroom; keeping their own: nursery (22.0).
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 1
room day night band morning evening
living 19.0 factory 17.0 factory 0.5 factory 06:00 factory 22:00 factory
bedroom 19.0 factory 16.0 own 0.5 factory 06:00 factory 22:00 factory
nursery 22.0 own 17.0 factory 0.3 own 06:00 factory 22:00 factory
Factory: day 19.0, night 17.0, band 0.5, day from 06:00 to 22:00.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 2
room (living, bedroom or nursery)> living
setting (day, night, band, morning or evening)> night
value, or factory to follow the factory again> 15.5
living night is now its own: 15.5. The other rooms are as they were.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 2
room (living, bedroom or nursery)> nursery
setting (day, night, band, morning or evening)> day
value, or factory to follow the factory again> factory
nursery day follows the factory again: 19.0.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 1
room day night band morning evening
living 19.0 factory 15.5 own 0.5 factory 06:00 factory 22:00 factory
bedroom 19.0 factory 16.0 own 0.5 factory 06:00 factory 22:00 factory
nursery 19.0 factory 17.0 factory 0.3 own 06:00 factory 22:00 factory
Factory: day 19.0, night 17.0, band 0.5, day from 06:00 to 22:00.
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 4
A day from 00:00, each room starting at 18.0 degrees with 5.0 outside:
room heating switched on lowest highest
living 7h 53m 91 15.0 19.6
bedroom 7h 55m 92 15.5 19.5
nursery 7h 56m 156 16.7 19.3
1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit
choice> 7
Bye.
What was typed (21 lines)
1
4
5
living
6
living
3
day
19.0
1
2
living
night
15.5
2
nursery
day
factory
1
4
7
Modules
The program as written, entry module first. Each module transpiles to its own Python file, which is what eml project run executes.
main.eml(entry)
eml# P115 thermostat: rooms with their own settings over factory defaults, a
# day minute by minute, one room hour by hour, and the band that keeps the
# heater from switching every minute.
import heat
def plain(s):
0 => i
len(s) => j
while i < j and s[i] == " ":
i + 1 => i
while j > i and s[j - 1] == " ":
j - 1 => j
return s[i:j]
def pad(s, n):
while len(s) < n:
s + " " => s
return s
def lpad(s, n):
while len(s) < n:
" " + s => s
return s
def keys():
return ["day", "night", "band", "morning", "evening"]
def shown(key, v):
if key == "morning" or key == "evening":
return heat.clock(v)
return heat.tenths(v)
def parse_tenths(t):
# "21.5" or "18" -> 215 or 180, or -1
"" => whole
"" => frac
False => dot
for c in t:
if c == "." and not dot:
True => dot
elif c >= "0" and c <= "9":
if dot:
frac + c => frac
else:
whole + c => whole
else:
return 0 - 1
if whole == "" or len(whole) > 2 or len(frac) > 1 or (dot and frac == ""):
return 0 - 1
if frac == "":
"0" => frac
return int(whole) * 10 + int(frac)
def parse_clock(t):
# "06:30" -> 390, or -1
if len(t) != 5 or t[2] != ":":
return 0 - 1
for i in [0, 1, 3, 4]:
if not (t[i] >= "0" and t[i] <= "9"):
return 0 - 1
int(t[0:2]) => h
int(t[3:5]) => m
if h > 23 or m > 59:
return 0 - 1
return h * 60 + m
def parse_setting(key, t):
if key == "morning" or key == "evening":
return parse_clock(t)
parse_tenths(t) => v
if key == "band":
if v > 30:
return 0 - 1
return v
if v < 50 or v > 300:
return 0 - 1
return v
def what(key):
if key == "morning" or key == "evening":
return "a time such as 06:30"
if key == "band":
return "a band from 0 to 3.0 degrees"
return "a temperature from 5.0 to 30.0 degrees"
def show_rooms(rooms):
" room day night band morning evening" ^0
for r in rooms:
" " + pad(r.name, 11) => line
for k in keys():
shown(k, r.get(k)) + " " + r.source(k) => cell
if k == "evening":
line + cell => line
else:
line + pad(cell, 14) => line
line ^0
("Factory: day " + heat.tenths(heat.Factory.day) + ", night " + heat.tenths(heat.Factory.night) + ", band " + heat.tenths(heat.Factory.band) + ", day from " + heat.clock(heat.Factory.morning) + " to " + heat.clock(heat.Factory.evening) + ".") ^0
def ask_room(rooms):
plain(input("room (living, bedroom or nursery)> ")) => n
if n == "":
return 0 - 1
for i in [0:len(rooms) - 1]:
if rooms[i].name == n:
return i
"Type living, bedroom or nursery." ^0
return 0 - 2
def ask_key():
plain(input("setting (day, night, band, morning or evening)> ")) => k
if k == "":
return ""
for x in keys():
if x == k:
return k
"Type day, night, band, morning or evening." ^0
return "!"
def day_fits(r, k, v):
# would the room's day still start before it ends with k set to v?
r.get("morning") => m
r.get("evening") => e
if k == "morning":
v => m
if k == "evening":
v => e
return m < e
def set_room(rooms):
ask_room(rooms) => i
if i == 0 - 1:
"Cancelled." ^0
return
if i == 0 - 2:
return
ask_key() => k
if k == "":
"Cancelled." ^0
return
if k == "!":
return
plain(input("value, or factory to follow the factory again> ")) => t
if t == "":
"Cancelled." ^0
return
rooms[i] => r
if t == "factory":
if k in r.own:
{} => keep
for x in r.own:
if x != k:
r.own[x] => keep[x]
keep => r.own
(r.name + " " + k + " follows the factory again: " + shown(k, r.get(k)) + ".") ^0
return
parse_setting(k, t) => v
if v < 0:
("Type " + what(k) + ".") ^0
return
if not day_fits(r, k, v):
"The day has to start before it ends." ^0
return
v => r.own[k]
(r.name + " " + k + " is now its own: " + shown(k, v) + ". The other rooms are as they were.") ^0
def set_factory(rooms):
ask_key() => k
if k == "":
"Cancelled." ^0
return
if k == "!":
return
plain(input("factory value> ")) => t
if t == "":
"Cancelled." ^0
return
parse_setting(k, t) => v
if v < 0:
("Type " + what(k) + ".") ^0
return
for r in rooms:
if r.source(k) == "factory" and not day_fits(r, k, v):
("The day has to start before it ends, in " + r.name + " too.") ^0
return
heat.set_factory(k, v)
[] => follow
[] => keep
for r in rooms:
if r.source(k) == "factory":
follow + [r.name] => follow
else:
keep + [r.name + " (" + shown(k, r.get(k)) + ")"] => keep
("Factory " + k + " is now " + shown(k, v) + ".") ^0
(" following it at once: " + listed(follow) + "; keeping their own: " + listed(keep) + ".") ^0
def listed(xs):
if len(xs) == 0:
return "none"
"" => out
for x in xs:
if out != "":
out + ", " => out
out + x => out
return out
def hm(minutes):
return str(int((minutes - minutes % 60) / 60)) + "h " + lpad(str(minutes % 60), 2) + "m"
def day(rooms):
"A day from 00:00, each room starting at 18.0 degrees with 5.0 outside:" ^0
" room heating switched on lowest highest" ^0
for r in rooms:
heat.run_day(r, 180, 50, 0 - 1) => d
(" " + pad(r.name, 11) + lpad(hm(d[0]), 7) + lpad(str(d[1]), 14) + lpad(heat.tenths(d[2]), 9) + lpad(heat.tenths(d[3]), 10)) ^0
def hours(rooms):
ask_room(rooms) => i
if i == 0 - 1:
"Cancelled." ^0
return
if i == 0 - 2:
return
rooms[i] => r
heat.run_day(r, 180, 50, 0 - 1) => d
(r.name + ", hour by hour (the temperature on the hour, minutes heating in the hour):") ^0
"" => line
0 => n
for h in [0:23]:
line + lpad(heat.clock(h * 60), 6) + lpad(heat.tenths(d[4][h][0]), 6) + lpad(str(d[4][h][1]), 4) => line
n + 1 => n
if n == 4:
line ^0
"" => line
0 => n
def bands(rooms):
ask_room(rooms) => i
if i == 0 - 1:
"Cancelled." ^0
return
if i == 0 - 2:
return
rooms[i] => r
(r.name + " over a day with other bands:") ^0
" band switched on heating lowest highest" ^0
for b in [0, 2, 5, 10]:
heat.run_day(r, 180, 50, b) => d
(" " + lpad(heat.tenths(b), 4) + lpad(str(d[1]), 14) + lpad(hm(d[0]), 10) + lpad(heat.tenths(d[5]), 9) + lpad(heat.tenths(d[6]), 10)) ^0
"The lowest and highest are in the day, from the moment the room first comes within the band. A narrow band holds the temperature close to the target and switches the heater far more often; a wide one switches less and lets it swing. Bands less than 0.3 degrees apart can come out the other way by a little, since the heater warms in steps of 0.2 degrees." ^0
[heat.Room("living"), heat.Room("bedroom"), heat.Room("nursery")] => rooms
160 => rooms[1].own["night"]
220 => rooms[2].own["day"]
3 => rooms[2].own["band"]
"== Thermostat ==" ^0
"Three rooms over the factory settings, each with its own changes, heated through a day." ^0
True => running
while running:
"" ^0
"1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit" ^0
plain(input("choice> ")) => choice
if choice == "1":
show_rooms(rooms)
elif choice == "2":
set_room(rooms)
elif choice == "3":
set_factory(rooms)
elif choice == "4":
day(rooms)
elif choice == "5":
hours(rooms)
elif choice == "6":
bands(rooms)
elif choice == "7":
False => running
else:
"Pick a number from 1 to 7." ^0
"Bye." ^0
Python projection (main.py)
import heat
def plain(s):
i = 0
j = len(s)
while i < j and s[i] == " ":
i = i + 1
while j > i and s[j - 1] == " ":
j = j - 1
return s[i:j]
def pad(s, n):
while len(s) < n:
s = s + " "
return s
def lpad(s, n):
while len(s) < n:
s = " " + s
return s
def keys():
return ["day", "night", "band", "morning", "evening"]
def shown(key, v):
if key == "morning" or key == "evening":
return heat.clock(v)
return heat.tenths(v)
def parse_tenths(t):
whole = ""
frac = ""
dot = False
for c in t:
if c == "." and not dot:
dot = True
elif c >= "0" and c <= "9":
if dot:
frac = frac + c
else:
whole = whole + c
else:
return 0 - 1
if whole == "" or len(whole) > 2 or len(frac) > 1 or dot and frac == "":
return 0 - 1
if frac == "":
frac = "0"
return int(whole) * 10 + int(frac)
def parse_clock(t):
if len(t) != 5 or t[2] != ":":
return 0 - 1
for i in [0, 1, 3, 4]:
if not (t[i] >= "0" and t[i] <= "9"):
return 0 - 1
h = int(t[0:2])
m = int(t[3:5])
if h > 23 or m > 59:
return 0 - 1
return h * 60 + m
def parse_setting(key, t):
if key == "morning" or key == "evening":
return parse_clock(t)
v = parse_tenths(t)
if key == "band":
if v > 30:
return 0 - 1
return v
if v < 50 or v > 300:
return 0 - 1
return v
def what(key):
if key == "morning" or key == "evening":
return "a time such as 06:30"
if key == "band":
return "a band from 0 to 3.0 degrees"
return "a temperature from 5.0 to 30.0 degrees"
def show_rooms(rooms):
print(" room day night band morning evening")
for r in rooms:
line = " " + pad(r.name, 11)
for k in keys():
cell = shown(k, r.get(k)) + " " + r.source(k)
if k == "evening":
line = line + cell
else:
line = line + pad(cell, 14)
print(line)
print("Factory: day " + heat.tenths(heat.Factory.day) + ", night " + heat.tenths(heat.Factory.night) + ", band " + heat.tenths(heat.Factory.band) + ", day from " + heat.clock(heat.Factory.morning) + " to " + heat.clock(heat.Factory.evening) + ".")
def ask_room(rooms):
n = plain(input("room (living, bedroom or nursery)> "))
if n == "":
return 0 - 1
for i in range(0, len(rooms)):
if rooms[i].name == n:
return i
print("Type living, bedroom or nursery.")
return 0 - 2
def ask_key():
k = plain(input("setting (day, night, band, morning or evening)> "))
if k == "":
return ""
for x in keys():
if x == k:
return k
print("Type day, night, band, morning or evening.")
return "!"
def day_fits(r, k, v):
m = r.get("morning")
e = r.get("evening")
if k == "morning":
m = v
if k == "evening":
e = v
return m < e
def set_room(rooms):
i = ask_room(rooms)
if i == 0 - 1:
print("Cancelled.")
return
if i == 0 - 2:
return
k = ask_key()
if k == "":
print("Cancelled.")
return
if k == "!":
return
t = plain(input("value, or factory to follow the factory again> "))
if t == "":
print("Cancelled.")
return
r = rooms[i]
if t == "factory":
if k in r.own:
keep = {}
for x in r.own:
if x != k:
keep[x] = r.own[x]
r.own = keep
print(r.name + " " + k + " follows the factory again: " + shown(k, r.get(k)) + ".")
return
v = parse_setting(k, t)
if v < 0:
print("Type " + what(k) + ".")
return
if not day_fits(r, k, v):
print("The day has to start before it ends.")
return
r.own[k] = v
print(r.name + " " + k + " is now its own: " + shown(k, v) + ". The other rooms are as they were.")
def set_factory(rooms):
k = ask_key()
if k == "":
print("Cancelled.")
return
if k == "!":
return
t = plain(input("factory value> "))
if t == "":
print("Cancelled.")
return
v = parse_setting(k, t)
if v < 0:
print("Type " + what(k) + ".")
return
for r in rooms:
if r.source(k) == "factory" and not day_fits(r, k, v):
print("The day has to start before it ends, in " + r.name + " too.")
return
heat.set_factory(k, v)
follow = []
keep = []
for r in rooms:
if r.source(k) == "factory":
follow = follow + [r.name]
else:
keep = keep + [r.name + " (" + shown(k, r.get(k)) + ")"]
print("Factory " + k + " is now " + shown(k, v) + ".")
print(" following it at once: " + listed(follow) + "; keeping their own: " + listed(keep) + ".")
def listed(xs):
if len(xs) == 0:
return "none"
out = ""
for x in xs:
if out != "":
out = out + ", "
out = out + x
return out
def hm(minutes):
return str(int((minutes - minutes % 60) / 60)) + "h " + lpad(str(minutes % 60), 2) + "m"
def day(rooms):
print("A day from 00:00, each room starting at 18.0 degrees with 5.0 outside:")
print(" room heating switched on lowest highest")
for r in rooms:
d = heat.run_day(r, 180, 50, 0 - 1)
print(" " + pad(r.name, 11) + lpad(hm(d[0]), 7) + lpad(str(d[1]), 14) + lpad(heat.tenths(d[2]), 9) + lpad(heat.tenths(d[3]), 10))
def hours(rooms):
i = ask_room(rooms)
if i == 0 - 1:
print("Cancelled.")
return
if i == 0 - 2:
return
r = rooms[i]
d = heat.run_day(r, 180, 50, 0 - 1)
print(r.name + ", hour by hour (the temperature on the hour, minutes heating in the hour):")
line = ""
n = 0
for h in range(0, 24):
line = line + lpad(heat.clock(h * 60), 6) + lpad(heat.tenths(d[4][h][0]), 6) + lpad(str(d[4][h][1]), 4)
n = n + 1
if n == 4:
print(line)
line = ""
n = 0
def bands(rooms):
i = ask_room(rooms)
if i == 0 - 1:
print("Cancelled.")
return
if i == 0 - 2:
return
r = rooms[i]
print(r.name + " over a day with other bands:")
print(" band switched on heating lowest highest")
for b in [0, 2, 5, 10]:
d = heat.run_day(r, 180, 50, b)
print(" " + lpad(heat.tenths(b), 4) + lpad(str(d[1]), 14) + lpad(hm(d[0]), 10) + lpad(heat.tenths(d[5]), 9) + lpad(heat.tenths(d[6]), 10))
print("The lowest and highest are in the day, from the moment the room first comes within the band. A narrow band holds the temperature close to the target and switches the heater far more often; a wide one switches less and lets it swing. Bands less than 0.3 degrees apart can come out the other way by a little, since the heater warms in steps of 0.2 degrees.")
rooms = [heat.Room("living"), heat.Room("bedroom"), heat.Room("nursery")]
rooms[1].own["night"] = 160
rooms[2].own["day"] = 220
rooms[2].own["band"] = 3
print("== Thermostat ==")
print("Three rooms over the factory settings, each with its own changes, heated through a day.")
running = True
while running:
print("")
print("1) rooms 2) set a room 3) change the factory settings 4) a day 5) hour by hour 6) the band 7) quit")
choice = plain(input("choice> "))
if choice == "1":
show_rooms(rooms)
elif choice == "2":
set_room(rooms)
elif choice == "3":
set_factory(rooms)
elif choice == "4":
day(rooms)
elif choice == "5":
hours(rooms)
elif choice == "6":
bands(rooms)
elif choice == "7":
running = False
else:
print("Pick a number from 1 to 7.")
print("Bye.")
heat.eml
eml# P115 thermostat - rooms that heat with a band around the target
# (hysteresis): the heater comes on at target - band and goes off at
# target + band, so it does not switch every minute. As in the corpus case
# thermostat-class-defaults, the factory settings live on the class and a
# room's own settings on the room: reading falls back to the class, live,
# and writing a room's setting never touches the class or another room.
#
# Temperatures are whole tenths of a degree; times are minutes from
# midnight. Each minute a heating room warms by 0.2 degrees, and a room
# warmer than outside cools by 0.1.
class Factory:
200 => day
170 => night
5 => band
360 => morning
1320 => evening
class Room:
def __init__(self, name):
name => self.name
{} => self.own
def get(self, key):
# the room's own value, or the factory's at the moment of asking
if key in self.own:
return self.own[key]
if key == "day":
return Factory.day
if key == "night":
return Factory.night
if key == "band":
return Factory.band
if key == "morning":
return Factory.morning
return Factory.evening
def source(self, key):
if key in self.own:
return "own"
return "factory"
def target(self, minute):
if minute >= self.get("morning") and minute < self.get("evening"):
return self.get("day")
return self.get("night")
def set_factory(key, value):
if key == "day":
value => Factory.day
elif key == "night":
value => Factory.night
elif key == "band":
value => Factory.band
elif key == "morning":
value => Factory.morning
else:
value => Factory.evening
def run_day(room, start, outside, band_override):
# [minutes heating, times switched on, lowest, highest, hourly
# [temperature at the hour, minutes heating in the hour], lowest and
# highest in the day period once the room has first come within the
# band, target - band to target + band - the swing the band allows,
# without the change from the night's temperature]; a band override of
# -1 uses the room's band
start => t
False => on
0 => heating
0 => switches
t => low
t => high
0 - 1 => day_low
0 - 1 => day_high
False => settled
[] => hours
0 => this_hour
room.get("band") => band
if band_override >= 0:
band_override => band
for m in [0:1439]:
if m % 60 == 0:
hours + [[t, 0]] => hours
0 => this_hour
room.target(m) => goal
if not on and t <= goal - band:
True => on
switches + 1 => switches
elif on and t >= goal + band:
False => on
if on:
t + 2 => t
heating + 1 => heating
hours[len(hours) - 1][1] + 1 => hours[len(hours) - 1][1]
elif t > outside:
t - 1 => t
if t < low:
t => low
if t > high:
t => high
if m >= room.get("morning") and m < room.get("evening"):
if t >= goal - band and t <= goal + band:
True => settled
if settled:
if day_low == 0 - 1 or t < day_low:
t => day_low
if t > day_high:
t => day_high
else:
False => settled
return [heating, switches, low, high, hours, day_low, day_high]
def tenths(t):
"" => sign
t => a
if t < 0:
"-" => sign
0 - t => a
return sign + str(int((a - a % 10) / 10)) + "." + str(a % 10)
def clock(m):
m % 60 => mm
int((m - mm) / 60) => h
str(h) => hs
if h < 10:
"0" + hs => hs
str(mm) => ms
if mm < 10:
"0" + ms => ms
return hs + ":" + ms
Python projection (heat.py)
class Factory:
day = 200
night = 170
band = 5
morning = 360
evening = 1320
class Room:
def __init__(self, name):
self.name = name
self.own = {}
def get(self, key):
if key in self.own:
return self.own[key]
if key == "day":
return Factory.day
if key == "night":
return Factory.night
if key == "band":
return Factory.band
if key == "morning":
return Factory.morning
return Factory.evening
def source(self, key):
if key in self.own:
return "own"
return "factory"
def target(self, minute):
if minute >= self.get("morning") and minute < self.get("evening"):
return self.get("day")
return self.get("night")
def set_factory(key, value):
if key == "day":
Factory.day = value
elif key == "night":
Factory.night = value
elif key == "band":
Factory.band = value
elif key == "morning":
Factory.morning = value
else:
Factory.evening = value
def run_day(room, start, outside, band_override):
t = start
on = False
heating = 0
switches = 0
low = t
high = t
day_low = 0 - 1
day_high = 0 - 1
settled = False
hours = []
this_hour = 0
band = room.get("band")
if band_override >= 0:
band = band_override
for m in range(0, 1440):
if m % 60 == 0:
hours = hours + [[t, 0]]
this_hour = 0
goal = room.target(m)
if not on and t <= goal - band:
on = True
switches = switches + 1
elif on and t >= goal + band:
on = False
if on:
t = t + 2
heating = heating + 1
hours[len(hours) - 1][1] = hours[len(hours) - 1][1] + 1
elif t > outside:
t = t - 1
if t < low:
low = t
if t > high:
high = t
if m >= room.get("morning") and m < room.get("evening"):
if t >= goal - band and t <= goal + band:
settled = True
if settled:
if day_low == 0 - 1 or t < day_low:
day_low = t
if t > day_high:
day_high = t
else:
settled = False
return [heating, switches, low, high, hours, day_low, day_high]
def tenths(t):
sign = ""
a = t
if t < 0:
sign = "-"
a = 0 - t
return sign + str(int((a - a % 10) / 10)) + "." + str(a % 10)
def clock(m):
mm = m % 60
h = int((m - mm) / 60)
hs = str(h)
if h < 10:
hs = "0" + hs
ms = str(mm)
if mm < 10:
ms = "0" + ms
return hs + ":" + ms