Obsidian Filter Empty Tasks

If you use Templates in Obsidian and you like to create an empty Task item (for quick typing) you will often find empty tasks showing up in your task lists.

The first “trick” is to exclude your Templates folder from Tasks. You can do this in the Tasks block as below. Replace “Templates” with wherever you store your templates.

```tasks
path does not include Templates
```

If you also leave empty tasks in your main notes you can filter them by including the below regex. (This basically matches the start of string and then immediately the end of string with nothing in between.

```tasks
description regex does not match /^$/
```

If you combine them together as below you should be good for most scenarios.

```tasks
description regex does not match /^$/
path does not include Templates
```

Before
After

IIS + PHP Minor Version Upgrade W/O Web Platform Installer

I have spent way too long trying to figure this out.

On a Windows Server with IIS, most people would recommend using the Web Platfrom Installer (WebPI), however only certain specific versions are available. If say you wanted to upgrade from PHP 7.4.13 to latest 7.4.27 WebPI can’t do it. You have to do it manually.

TLDR: Download the new version, compare the php.ini files and swap the directories over.

Instructions

Grab the version of PHP that you are after from PHP directly – https://windows.php.net/download#php-7.4 (you will want the Non-Thread-Safe version).

Make note of and install any dependencies like Microsoft C++ Redist (here – https://aka.ms/vs/16/release/VC_redist.x64.exe)

Locate your current PHP installation Directory. For me (since I installed PHP originally from WebPI, it was C:\Program Files\PHP).

Extract your download version NEXT to the existing version and rename with -new. Like below.

Directory structure after extracting and renaming

You will want Notepad++ (with the Compare add-on) or a similar Text File Comparison tool.

You want to compare the CURRENT php.ini and the NEW php.ini-production for changes.

IMPORTANT: If you have used WebPI for the original PHP installation, most of the changes will be at the very end of the current PHP.ini. Especially things like Extensions. Do not miss these as I did.

Copy across all relevant settings from the CURRENT to the NEW php.ini-production (and make note of any newly introduced settings).

Copy and rename the NEW php.ini-production to php.ini within the NEW dir.

Shutdown IIS from IIS Manager (the Server level not the site level).

Swap the PHP Directory names round.

After Renaming Directories

Startup IIS.

Acknowledgments

https://www.itgeekrambling.co.uk/wordpress-manual-update-of-php-7-x-on-iis/

Reverse Engineering Hozelock Cloud Controller

Overview

I recently purchased the Hozelock Cloud Controller to automatically water my garden. While trying to integrate it with OpenHab I discovered various things about the REST API Service that are concerning and useful. – https://www.hozelock.com/product/cloud-controller/

All code can be found https://github.com/martynjsimpson/HozelockAPI

Basics

The Cloud Controller is made up of two parts the “Hub” which connects to your Router/ LAN, and the Cloud Controller which connects to the Hub (and is the bit that is connected to your tap.

Discovery

By using their Cloud Support page http://cloudsupport.hoz3.com/ and entering a valid HubID you can retrieve the status of your Hub and Controller. Inspecting the traffic from the browser shows a GET request to http://hoz3.com/restful/support/hubs/{hubId} returning a JSON Array.

The first thing I noticed was there is no Authentication on the endpoint – you just need a valid HubID.

If we make a direct call to http://hoz3.com/restful/support/hubs/xxxxxx (i.e an Invalid hubId) The endPoint is nice enough to tell us the Regex of valid HubID’s

GET http://hoz3.com/restful/support/hubs/xxxxxx

{
"errorCode": 134611463,
"httpStatus": 400,
"errorMessage": "GET request to '/restful/support/hubs/xxxxxx' failed due to invalid parameter 'hubID': Expected a valid Hub ID matching [0-9A-Z]{6}",
"cause": {
"method": "GET",
"url": "/restful/support/hubs/xxxxxx",
"contentType": null
}
}

If somebody want’s to do the math on the number of permutations this allows I would appreciate it!

Oh did you notice that all of this is over HTTP? So SSL Is supported but only if you request it. It is not forced or redirected from HTTP. Throughout this post, I will probably mix HTTP and HTTPS but assume that everything is possible over HTTP and HTTPS unless I say otherwise.

Now let’s have a look at what else it supports.

Hub

OPTIONS https://hoz3.com/restful/support/hubs/{HubID}
{
"errorCode": 0,
"allowedMethods": [
"GET",
"OPTIONS"
]
}

Nothing really useful here we can do but the data response within the Hub element CAN contain some sensitive information such as location if the user has set it. (The Setup Wizards asks for your location at install time to determine weather at your address). It appears to just be City, Country, Local time and Time Zone.

Let’s keep going through the rest of the JSON Response to the GET /{hubId}

Schedules

GET https://hoz3.com/restful/support/hubs/{hubId}/schedules

Returns an array of all Schedules for the Hub. Note: This does NOT mean the schedule is applied to the controller. (Sample below with some days removed). This is set to water at Sunrise and Sunset hence two entries per day.

TimeStamps appear to be using Unix Epoch Milliseconds. -2000 appears to mean Sunrise, -1000 appears to mean Sunset (or an offset). Duration is in Milliseconds (15 mins here)

[
{
"scheduleID": "{scheduleId}",
"name": "{scheduleName}",
"description": null,
"scheduleDays": {
"Monday": {
"dayOfWeek": "Monday",
"wateringEvents": [
{
"startTime": -2000,
"endTime": 898000,
"duration": 900000,
"enabled": true
},
{
"startTime": -1000,
"endTime": 899000,
"duration": 900000,
"enabled": true
}
]
},
"Thursday": {
"dayOfWeek": "Thursday",
"wateringEvents": [
{
"startTime": -2000,
"endTime": 898000,
"duration": 900000,
"enabled": true
},
{
"startTime": -1000,
"endTime": 899000,
"duration": 900000,
"enabled": true
},
....... removed
]
}
}
}
]

You can also get a specific schedule from the array by appending the scheduleID to the URL.

GET https://hoz3.com/restful/support/hubs/{hubId}/schedules/{scheduleId}

Looking at what can be done at the Schedules element.

OPTIONS https://hoz3.com/restful/support/hubs/{hubId}/schedules/
{
"errorCode": 0,
"allowedMethods": [
"GET",
"HEAD",
"OPTIONS"
]
}

Not much of use here although HEAD could be useful. Looking at the specific Schedule element.

OPTIONS https://hoz3.com/restful/support/hubs/{hubId}/schedules/{scheduleId}
{
"errorCode": 0,
"allowedMethods": [
"DELETE",
"GET",
"HEAD",
"PATCH",
"POST",
"PUT",
"OPTIONS"
]
}

This is much more useful with POST, PUT and PATCH allowing us to create, “upsert” and update Schedule information. Even DELETE is available.

I was able to adjust the schedule using a PATCH method (just changed the name) but I am yet to fully work out the structure of create, updating and deleting a schedule.

Controllers

One or more controllers can be associated with a single hub (perhaps one for the front garden and one for the rear).

GET https://hoz3.com/restful/support/hubs/{hubId}/controllers
{
"controllers": [
{
"name": "{controllerName}",
"image": null,
"controllerID": "0",
"scheduleID": "{scheduleId}",
"schedule": {
"scheduleID": "{scheduleId}",
"name": "{scheduleName}",
"description": null,
"scheduleDays": {
"Monday": {
"dayOfWeek": "Monday",
"wateringEvents": [
{
"startTime": -2000,
"endTime": 898000,
"duration": 900000,
"enabled": true
},
{
"startTime": -1000,
"endTime": 899000,
"duration": 900000,
"enabled": true
}
]
},
....... trimmed
}
}
},
"hasWaterNowEvent": false,
"pause": null,
"adjustment": null,
"waterNowEvent": null,
"currentWateringEvent": null,
"nextWateringEvent": {
"startTime": 1556219700000,
"endTime": 1556220600000,
"duration": 900000,
"enabled": true
},
"lastCommunicationWithServer": 1556211067000,
"nextCommunicationWithServer": 1556212140000,
"batteryStatus": "OK",
"signalStrength": "GOOD",
"overrideScheduleDuration": null,
"isChildlockEnabled": false,
"isWatering": false,
"isPanelRemoved": false,
"isTested": true,
"isAdjusted": false,
"isScheduleUpToDate": true,
"isPaused": false
}
],
"inPairingMode": false,
"lastServerContactDate": 1556211067000,
"hubResetRequired": false,
"controllerResetRequired": false,
"isUresponsive": false
}
}

Again this returns an Array of all Controllers and you can address a specific Controller by appending the controllerId to the URL such as /{hubId}/controllers/{controllerId}

Both the controllers and controller/{controllerId} endpoint only support GET, HEAD and OPTIONS.

OPTIONS https://hoz3.com/restful/support/hubs/{hubId}/controllers/{controllerId}/
OR
OPTIONS https://hoz3.com/restful/support/hubs/{hubId}/controllers

{
"errorCode": 0,
"allowedMethods": [
"GET",
"HEAD",
"OPTIONS"
]
}

Actions

This section is pretty much entirely credited to anthonyangel (see credits)

An undocumented part of the API’s called Actions exists which allow you to submit tasks for the Hub (and thus controller) to pick up.

GET http://hoz3.com/restful/support/hubs/{hubId}/controllers/actions/

{
"errorCode": 0,
"actions": [
"pause",
"unpause",
"adjust",
"unadjust",
"waterNow",
"stopWatering",
"setMode",
"ping"
]
}

Using the constructs worked out by anthonyangel we can send a POST with a body to issue a waterNow command.

POST http://hoz3.com/restful/support/hubs/{hubId}/controllers/actions/waterNow

Request Body
{
"controllerIDs":[{controllerId}],
"duration":300000
}

Response
{
"errorCode": 0
}

Sure enough after the up to 20min wait time for the controller to poll the hub the alert comes up on my phone saying it is watering.

Pause – Will pause watering for x number of days
Unpause – Will remove a Pause
Adjust – I believe this endpoint is used to increase the percentage of watering time for a period of time.
UnAdjust – Undo the adjust
waterNow – Starts a watering session
stopWatering – Stops the watering
setMode – Not sure but is expecting “mode” parameter
ping – Despite ping being listed as available if you issue a POST or a GET the response says that ping is not implemented.

With all of these if you bulld the payload with just controllerIds the error response will kindly tell you what else you are missing. It helps if you have the app to hand to figure out what you put in the UI as to what might be in the API.

Conclusions

As is the way with most new IOT it appears that security generally lapses. Sure do we really care if our irrigation system is changed (maybe you can waste some water or kill my plants)? That said the model of Controller –> Hub –> “cloud” means that it should not be possible to compromise the rest of your LAN.

Basic security such as forcing SSL seems to have been missed – it doesn’t take much to enable it server side although I will admit without knowing the OS on the Hub this could be more difficult than I am allowing.

Authentication on the Endpoints even for GET operations is a must in today’s world. Relying on security by obscurity is not wise or feasible. Other people have reported no rate limiting on the API calls so brute forcing HubId’s is technically feasible. OAuth, Certificate-based authentication or even Basic Auth again is trivial to implement server-side but I admit client side on the Hub might be the reason this was never done.

One other thing to consider is should companies like Hozelock have developer portals allowing them access to API Documentation, to register OAuth application ID’s and contribute. I certainly wouldn’t have gone digging as far as I did if the documentation was readily available. This is not a substitute for actual security mind. IOT is all about integration and while the average consumer of this product is likely to not be the Developer type, there are developers out there. I want to integrate my watering system with the rest of my home automation, I want to implement Alexa/ Google Assist skills if companies don’t provide them and I certainly want to implement the number 1 missing feature on this application – automatic adjustments for weather. It’s all good telling me it has been hot for 3 days so “you might want to adjust your watering schedule” – you adjust my watering schedule, you know the weather better than me, you have the information, just do it for me.

Overall I love the product, I suck at remembering to water the garden and my beds have never looked so good. It was a pain to initially install, due to I suspect previous attempts to pair the controller to the hub, but this was fixed by doing a factory reset on everything and starting from scratch. Keep the alerts turned on so you are alerted to watering events (or lack of) and you will soon see if somebody is changing your configuration or watering your garden for you.

Hozelock like many other brands appears to be rushing into the IOT space to be “first to market” or even to play catch up against their competitors. Security is often left behind in an effort to get to market or perhaps even not considered in the design phase. It is critical that companies consider the implications of their security design decisions for IOT devices just as they would for their own internal applications.

I searched around for somewhere to report this to but couldn’t find anything.

Credits

Original inspiration to look into this comes from https://community.home-assistant.io/t/having-hozelock-cloud-controller-kit-intergration/55694/3
anthonyangel. He apparently reported the lack of security around Jul 2018 and it doesn’t look like much came of it.

Comments / feedback

Email me