ELK M1 Interface

Hmm… I’ll have to stare at the code a bit longer to get more familiar. Just a couple of questions…

Shouldn’t this change be made, or are you looking for “no temperature sensor” responses?

diff --git a/PyElk/Elk.py b/PyElk/Elk.py
index 218eb8b..24e75d8 100644
--- a/PyElk/Elk.py
+++ b/PyElk/Elk.py
@@ -401,7 +401,7 @@ class Elk(object):
                 event._type = Event.EVENT_TEMP_REQUEST
                 event._data_str = '0' + format(z,'02')
                 self.elk_event_send(event)
-                reply = self.elk_event_scan(Event.EVENT_TEMP_REQUEST_REPLY, [event._data_str,'7' + format(z,'02')])
+                reply = self.elk_event_scan(Event.EVENT_TEMP_REQUEST_REPLY, [event._data_str,'0' + format(z,'02')])
                 if (reply):
                     _LOGGER.debug('scan_zones : got Event.EVENT_TEMP_REQUEST_REPLY')
                     group = int(reply._data[0])
@@ -469,7 +469,7 @@ class Elk(object):
                 event._type = Event.EVENT_TEMP_REQUEST
                 event._data_str = '1' + format(k,'02')
                 self.elk_event_send(event)
-                temp_reply = self.elk_event_scan(Event.EVENT_TEMP_REQUEST_REPLY, [event._data_str,'7' + format(k,'02')])
+                temp_reply = self.elk_event_scan(Event.EVENT_TEMP_REQUEST_REPLY, [event._data_str,'1' + format(k,'02')])
                 if (reply):
                     _LOGGER.debug('scan_keypads : got Event.EVENT_TEMP_REQUEST_REPLY')
                     group = int(temp_reply._data[0])

At least for zones, I see that after scanning the zones and discovering the “temperature” zones, a query will be sent for that zone, but I’m not sure if the response actually gets handled…? I’ll add the log message as suggested to see how that appears.

And more generally, what’s the process that periodically polls for temperature measurements? In the code that I wrote some years ago (somewhat quick and dirty, happens to be in Lua), would transmit ‘st’ Request Temperature messages every few minutes to elicit a response.

I think these changes are probably necessary, well at least the non-debugging stuff… Having problems trying to get hass to find the new library, but it seems to be doing the right thing using using bin/PyElk-test.py

Feels like its close now…

diff --git a/PyElk/Elk.py b/PyElk/Elk.py
index 218eb8b..e6e2672 100644
--- a/PyElk/Elk.py
+++ b/PyElk/Elk.py
@@ -309,6 +309,7 @@ class Elk(object):
                         continue
                     elif (event._type == Event.EVENT_TEMP_REQUEST_REPLY):
                         """Temp sensor update"""
+                        _LOGGER.debug('elk_queue_process - Event.EVENT_TEMP_REQUEST_REPLY')
                         group = int(event._data[0])
                         number = int(event._data_str[1:3])
                         if (group == 0):
@@ -401,15 +402,17 @@ class Elk(object):
                 event._type = Event.EVENT_TEMP_REQUEST
                 event._data_str = '0' + format(z,'02')
                 self.elk_event_send(event)
-                reply = self.elk_event_scan(Event.EVENT_TEMP_REQUEST_REPLY, [event._data_str,'7' + format(z,'02')])
+                reply = self.elk_event_scan(Event.EVENT_TEMP_REQUEST_REPLY, [event._data_str,'0' + format(z,'02')])
                 if (reply):
                     _LOGGER.debug('scan_zones : got Event.EVENT_TEMP_REQUEST_REPLY')
                     group = int(reply._data[0])
                     number = int(event._data_str[1:3])
-                    if ((group == '0') and (number == z)):
+                    if ((group == 0) and (number == z)):
+                        _LOGGER.debug('scan_zones : temperature group={} number={} rawtemp={}'.format(group, number,
+                                                                                                      reply._data_str[3:6]))
                         self.ZONES[number].unpack_event_temp_request_reply(reply)
                     else:
-                        _LOGGER.debug('scan_zones : error reading temperature, ' + str(number))
+                        _LOGGER.debug('scan_zones : error reading temperature, "' + str(number) + '"')

         z = 1
         while z < 209:
@@ -469,13 +472,16 @@ class Elk(object):
                 event._type = Event.EVENT_TEMP_REQUEST
                 event._data_str = '1' + format(k,'02')
                 self.elk_event_send(event)
-                temp_reply = self.elk_event_scan(Event.EVENT_TEMP_REQUEST_REPLY, [event._data_str,'7' + format(k,'02')])
+                temp_reply = self.elk_event_scan(Event.EVENT_TEMP_REQUEST_REPLY, [event._data_str,'1' + format(k,'02')])
                 if (reply):
                     _LOGGER.debug('scan_keypads : got Event.EVENT_TEMP_REQUEST_REPLY')
                     group = int(temp_reply._data[0])
                     number = int(temp_reply._data_str[1:3])
-                    if ((group == '0') and (number == k)):
+                    if ((group == 1) and (number == k)):
                         self.KEYPADS[keypad_number].unpack_event_temp_request_reply(temp_reply)
+                        _LOGGER.debug('scan_keypads : temperature group={} number={} rawtemp={}'.format(group, number,
+                                                                                                      temp_reply._data_str[3:6]))
+
                     else:
                         _LOGGER.debug('scan_keypads : error reading temperature, ' + str(number))

@lmamakos
We’re looking both for the exact response we want (event._data_str, which is the same as group + device number, in the data passed to elk_event_scan you could replace event._data_str with '1' + format(k,'02') for example in scan_keypads, and perhaps that is what the code should be simply to be more clear what it is doing, but it would be functionally identical) as well as the group 7 version of the response response. We then double check the group / number, if valid we pass it to the zone/keypad object’s unpack_event_temp_request_reply, otherwise we don’t (at the moment we only log failure, not do anything else, we look for failure just so we dont wait until timeout on every request, which is by default 10s in elk_event_scan). Without checking for group 7 responses we’d take an extra minute or two to scan the keypad temps for example at startup.

At least, that’s the intended functionality. I’m kinda flying blind at the moment :slight_smile:

@lmamakos
I think I overlooked your last reply due to similar timing as when I was writing mine…

You’re right, since I took int(reply._data[0]) etc, I should compare to 0 not ‘0’ in that if ((group == ... statement.

Though, I still don’t think the reply = self.elk_event_scan(... needs to be changed. At that call, event._data_str is the same as '0' + format(z,'02') (so you’re listing the same thing to match against twice instead of once with group 0 and once with group 7). However, that change will only cause problems on the keypads as long as you don’t have a zone configured for temperature but nothing connected to it. But getting the group 7 response is necessary for keypads, otherwise keypads without sensors on them will cause a timeout (it shouldn’t break anything, but take longer to start up).

Try it without those two changes to the elk_event_scan(...) calls ?

Also noticed another error, in scan_keypads

                temp_reply = self.elk_event_scan(...
                if (reply):

should instead be

                temp_reply = self.elk_event_scan(...
                if (temp_reply):

Otherwise it will always be True because reply already exists (contains Event.EVENT_KEYPAD_AREA_REPLY from further up in the scan_keypads code). If for some reason we failed to get a reply, we’ll crash inside the if block from trying to access non-existent members of bool.

Strangely, I’m not seeing group 7 responses any more. I get group 0 or 1 with raw temp of 000. This might mean the threshold to auto hide should be changed to hide when <= -40 for keypads and <= -60 for zones (though realistically we could probably just do <= -40, since if anyone really needs to measure that cold they can just override the hiding via YAML … )

But now I’m not sure how I was getting group 7 replies. Maybe an earlier version of my PyElk code was just reading incorrectly (wrong offset) and I was seeing the wrong part of the data … ?

So perhaps all this group 7 handling can be tossed out, since now I can’t reproduce it even happening any more… :scream:

@lmamakos

Hopefully I have fixed things this time. Feel free to test again against latest Githubs / PyPi.

Also, tomorrow I will finally be back at the physical location of my Elk so I can try to get a temperature probe hooked up (if indeed I have one at hand) so I can test things more directly myself :stuck_out_tongue:

Great work on this!

Ive also been using a forked elkington and a node.js app and would love to go full python.

Turns out I don’t have an Elk temp sensor after all, and I don’t have any KP1’s so I can’t directly test temp readings.

Just pulled the new version of ha-elkm1 from git (and apparently the new pyelk automagically) and the zone temperatures are showing up correctly now for the two keypads that have temperature sensors.

The zone temperature sensor is missing, will poke at that to see if there’s something obvious I can see there.

This seems to be working now, not sure exactly why. All 3 of my Elk temperature sensors show up, one zone sensor and two keypad sensors. And the temperature values appear to be correct, too.

I don’t know if these values will stay updated unless they are polled for. In my situation, I have other software generating polls, so hass is going to see those responses that magically arrive…

@lmamakos Weird, nothing with Switch has changed for a while.

Try nuking your (HASS config dir)/deps/PyElk* (assuming it exists, at least running under docker this is where it auto installs PyPi packages that aren’t already on your system / in the docker image) to make sure it pulls a totally clean copy of PyElk, and restart HASS. I haven’t had it specifically mangle an existing copy, but I have had it fail to pull the new version and then refuse to start without doing that (because the required version and installed didn’t match), so it’s at least something to try. For all I know, my current version numbering scheme doesn’t grok properly (since I’m up to ‘0.1.1.dev11’ now, perhaps something about that is no bueno), though it works here :confused:

In your case you might want to also use pypi to uninstall any installed versions of PyElk too, in addition to nuking anything in (config)/deps/PyElk*

Oh, I wonder what happens when stuff is installed in the python autoenv (when I invoke setup.py) and when hass fetches and installed them in the deps/ directory. Hmm… I can see the source of confusion.

I need to go see how setuptools works.

@lmamakos I think that if you’re running it under your system “normally” (i.e. not in Docker) then it can use the system installed version, but I’m not sure. And if it was autoinstalled at some point, I think it does it to config/deps regardless (though I might be wrong) so at some point things might get confusing :smiley:

@lmamakos I’ve just updated the Github repos and PyPi. You should hopefully now be able to see your thermostats as a temperature sensor device in HASS. Hopefully I haven’t broken anything, I’ve been moving code around and cleaning things up, etc. Let me know how it goes. I haven’t broken anything on my end, but I have zero temperature devices to actually test with and trying to fake it as we’ve learned already doesn’t always work as expected :smiley:

I may or may not try to knock out proper thermostat (“Climate”) device support in HASS tomorrow. I probably won’t be able to work on anything all next week, too, but after that I should be able to spend some more time implementing / improving things.

@BioSehnsucht, thanks! I will try this later today and see how it works out. I may look at what it takes to implement lights, as I have some X10 lights (and a few X-10 motion-sensitive flood lamps that also transmit motion detected events as X-10 ON and OFF commands…)

The thermostats, like all of the other temperature sensors will also need to be polled periodically. Any thoughts on how that fits in?

This seems to work! I did have to do this:

diff --git a/setup.py b/setup.py
index 8d18f03..634319e 100644
--- a/setup.py
+++ b/setup.py
@@ -14,6 +14,8 @@ setup(name='PyElk',
         license='Apache License 2.0',
         packages=[
             'PyElk',
+            'PyElk.Node',
+            'PyElk.Thermostat',
             'PyElk.Event',
             'PyElk.Zone',
             'PyElk.Output',

I need to go update my groups and stuff since the names have changed (and I think the new style is an improvement). I do see the correct temperature reported from my one thermostat, so yay!

Strangely, my zone temperature sensor disappeared. This is weird because running the test script seems to indicate that it was found. I won’t have time to investigate this further until later tonight and I turn on debugging output in hass to see if that reveals anything interesting.

@lmamakos Oops, I had manually updated my HASS install and forgot to test it with it needing to install PyElk, you’re right that I forgot to add those packages so the current PyPi would be quite broken!

Did the Zone temp disappear completely (not even in states) or is it just hidden?

@lmamakos I’m working on thermostat support. I’ve got it sort of working, though I can’t test it too well by just inserting fake packets into PyElk :smiley:

One issue is apparently the existing thermostat ‘more info’ widget can’t handle having heat/cool setpoints instead of a single set point. At the moment, you can’t really set the temperature from HASS, but you should be able to see it and fiddle the modes hopefully. It’s still a WIP. The existing HASS widget could possibly handle setting one set point if in heat or cool only (though I haven’t yet implemented it), but in auto mode you can’t set both. This is true for my EcoBee too…