Väntar fortfarande på komponenterna så jag började med GUI till servern. Jag trodde jag hade beställt en 7" skärm men det visade sig att jag tydligen beställde 5" versionen, så det blir ju lite mindre... Blir nog bra ändå. Den skall monteras på väggen bredvid soffan i alla fall, sen kommer jag ju styra allt via telefonen och PIR-sensorer också.
Tar galet mycket tid att designa det i Java så att det rescalar sig bra till olika upplösningar. Så här långt har jag kommit än i alla fall. Väderprognos på vänster sida. Där skall också in temperatur inne och ute från mina givare. Till höger är TV-tablå för gratiskanalerna. I bottnen är knappar för att välja belysningsläge.
printscreen.png
Tror jag kommer ha den med vit text såhär istället. Funderar dock på om röd text kanske är bekvämare när det är mörkt?
printscreen2.png
Väderprognoserna finns som XML utgivna av Yahoo, så de är lätta att ladda ned och parse'a. Skrev bara ihop en enkel klass för att ladda ned ett dokument från webben:
Kod: Markera allt
public class WebDownloader {
String url;
public WebDownloader(String url) {
this.url = url;
}
public String getSource() {
try {
URL webPageURL = new URL(url);
URLConnection connection = webPageURL.openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
String line;
StringBuilder source = new StringBuilder();
while ((line = reader.readLine()) != null)
source.append(line);
reader.close();
return source.toString();
} catch (MalformedURLException e) {
System.out.println("Error: Malformed URL for source download");
} catch (IOException e) {
System.out.println("Error: IO Exception at source download");
}
return "";
}
}
Sen parse'ar jag det med DOM:
Kod: Markera allt
public class DownloadWeather implements Runnable{
private static final String WEATHER_URL = "https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22lund%2C%20skane%2C%20se%22)%20and%20u='c'&format=xml&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys";
private WeatherMonitor mon = new WeatherMonitor();
@Override
public void run() {
WebDownloader downloader = new WebDownloader(WEATHER_URL);
while (mon.isUpdating()) {
String source = downloader.getSource();
extractWeather(source);
try {
Thread.sleep(600000); // 10 minute
} catch (InterruptedException e) {
}
}
}
public WeatherMonitor getMonitor(){
return mon;
}
private void extractWeather (String source) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
StringBuilder xmlBuilder = new StringBuilder();
xmlBuilder.append(source);
ByteArrayInputStream stream = new ByteArrayInputStream(
xmlBuilder.toString().getBytes("UTF-8")
);
Document document = builder.parse(stream);
NodeList dayReports = document.getElementsByTagName("yweather:forecast");
for (int i = 0; i < dayReports.getLength(); i++){
Node n = dayReports.item(i);
DateFormat df = new SimpleDateFormat("dd MMM yyyy");
Date date = df.parse(((Element)n).getAttribute("date"));
mon.addReport(new WeatherReport(date,
Integer.parseInt(((Element) n).getAttribute("high")),
Integer.parseInt(((Element)n).getAttribute("low")),
((Element)n).getAttribute("text")
));
}
} catch (ParserConfigurationException e) {
System.out.println("Error: Parser configuration");
} catch (UnsupportedEncodingException e) {
System.out.println("Error: Unsupported encoding");
} catch (SAXException e) {
System.out.println("Error: SAX");
} catch (IOException e) {
System.out.println("Error: IO Exception DOM");
} catch (ParseException e) {
System.out.println("Error: date parsing");
}
}
}
Använder regex för att identifiera fem typer av väder utifrån beskrivningstexten än så länge.
Kod: Markera allt
// Class for storing a single days weather report
public class WeatherReport {
public Date date;
public int high, low, type;
public String text;
public WeatherReport(Date date, int high, int low, String text) {
this.date = date;
this.high = high;
this.low = low;
this.text = text;
setType();
}
// Extract the type of weather from the text
private void setType(){
if (text.toLowerCase().contains("snow"))
type = WeatherMonitor.SNOW;
else if (text.toLowerCase().contains("rain") || text.toLowerCase().contains("showers"))
type = WeatherMonitor.RAIN;
else if (text.toLowerCase().contains("partly cloudy"))
type = WeatherMonitor.PARTLY_CLOUDY;
else if (text.toLowerCase().contains("cloudy"))
type = WeatherMonitor.CLOUDY;
else if (text.toLowerCase().contains("sunny"))
type = WeatherMonitor.SUNNY;
else
type = -1; // Unknown type, use raw text instead of picture
}
// Write to string
public String toString(){
return date.toString() + ": " +
high + "'C/" +
low + "'C " +
text + ", " +
type + "\n";
}
// Clone
public WeatherReport clone(){
return new WeatherReport(date,high,low,text);
}
}
Sparas i hashmap i en monitor som GUI't kan komma åt.
Kod: Markera allt
package Weather;
import java.util.*;
public class WeatherMonitor {
private Map<String, WeatherReport> reports = new HashMap<>();
private boolean update = true;
// Weather states
public static final int SNOW = 0;
public static final int RAIN = 1;
public static final int PARTLY_CLOUDY = 2;
public static final int CLOUDY = 3;
public static final int SUNNY = 4;
public synchronized void addReport(WeatherReport report){
reports.put(report.date.toString(), report);
removeOldReports();
}
public synchronized WeatherReport getReportDaysFromNow(int days){
Date day = new Date();
day.setTime(day.getTime() + (long)86400000*days);
String sDate = startOfDay(day).toString();
if (reports.get(sDate) != null)
return reports.get(sDate).clone();
return null;
}
public synchronized String toString(){
String s = "";
for (WeatherReport report : reports.values()){
s += report.toString() + "\n";
}
return s;
}
public synchronized void stop(){
update = false;
}
public synchronized boolean isUpdating(){
return update;
}
private void removeOldReports(){
Date startOfToday = startOfToday();
Iterator i = reports.entrySet().iterator();
while (i.hasNext()){
if (((Map.Entry<String, WeatherReport>)i.next()).getValue().date.getTime() < startOfToday.getTime())
i.remove();
}
}
private Date startOfToday(){
return startOfDay(new Date());
}
private Date startOfDay(Date date){
Calendar startOfDay = Calendar.getInstance();
startOfDay.setTime(date);
startOfDay.set(Calendar.HOUR_OF_DAY, 0);
startOfDay.set(Calendar.MINUTE, 0);
startOfDay.set(Calendar.SECOND, 0);
startOfDay.set(Calendar.MILLISECOND, 0);
return startOfDay.getTime();
}
}
TV programmen var lite jobbigare för det verkar inte finnas något API för svenska kanaler. Men det gick snabbt att slänga ihop en enkel crawler som hämtar informationen från tv.nu
Kod: Markera allt
public class TvGuideDownloader implements Runnable{
private static final String URL = "http://www.tv.nu/";
ArrayList<String> channels = new ArrayList<>();
TvGuideMonitor mon = new TvGuideMonitor();
public TvGuideDownloader(String [] channels) {
ArrayList<String> ch = new ArrayList<String>(Arrays.asList(channels));
ch.forEach((c)->this.channels.add(c.toLowerCase()));
}
public TvGuideMonitor getMonitor(){
return mon;
}
@Override
public void run() {
WebDownloader downloader = new WebDownloader(URL);
while (true) {
// Download source from page
String source = downloader.getSource();
// Split by channels
HashMap<String, String> channelData = splitChannels(source);
// Extract program info
ArrayList<TvProgram> programs = extractPrograms(channelData);
// Filter
programs.removeIf(p -> !channels.contains(p.getChannel().toLowerCase()));
programs.removeIf(p -> p.getTime().getTime() < (new Date()).getTime());
// Put in monitor
mon.setList(programs);
// Sleep
try {
Thread.sleep(60000); // 1 minute
} catch (InterruptedException e) {
}
}
}
private HashMap<String,String> splitChannels(String source) {
String regexp = "zeta color-gentle\\\">";
String [] s = source.split(regexp);
HashMap<String,String> channels = new HashMap<>();
for (int i = 0; i < s.length; i ++)
if (i != 0){
String [] channelData = s[i].split("</span>",2);
channels.put(channelData[0], channelData[1]);
}
return channels;
}
private ArrayList<TvProgram> extractPrograms(HashMap<String,String> channelData){
ArrayList<TvProgram> programs = new ArrayList<>();
String splitPoint = "data-id";
Pattern rProgramTitle = Pattern.compile("(?<=data-title=\")(.*)(?=\")");
Pattern rProgramTime = Pattern.compile("(?<=data-start-time-unix=\")(.*)(?=\")");
for (Map.Entry<String,String> entry : channelData.entrySet()){
String [] ps = entry.getValue().split(splitPoint);
for (String data : ps){
String title = "";
String time = "";
Matcher mTitle = rProgramTitle.matcher(data);
if (mTitle.find())
title = replaceHtmlCodes(mTitle.group().split("\"")[0]);
Matcher mTime = rProgramTime.matcher(data);
if (mTime.find())
time = mTime.group().split("\"")[0];
if (time.length() > 0 && title.length() > 0)
programs.add(new TvProgram(entry.getKey(), title, new Date(Long.parseLong(time) * 1000)));
}
}
return programs;
}
private String replaceHtmlCodes(String text){
String [] symbol = {" ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/"};
for (int i = 32; i <= 47; i ++){
text = text.replaceAll("\\&#" + Integer.toString(i) + ";", symbol[i-32]);
text = text.replaceAll("\\�" + Integer.toString(i) + ";", symbol[i-32]);
}
return text;
}
}